重载与覆盖
的选择正确的实施正如您所指出的,of 方法是在运行时完成的,现在要调用的方法的签名是在编译时决定的。
编译时重载方法选择
The Java语言规范(JLS) 第 15.12 节方法调用表达式详细解释了编译器选择要调用的正确方法所遵循的过程。
在那里,你会注意到这是一个编译时任务。 JLS 在第 15.12.2 款中表示:
此步骤使用方法名称 and 参数表达式的类型找到既可用又适用的方法
可能有不止一种这样的方法,在这种情况下选择最具体的一种。
通常,如果可变参数方法与其他候选方法竞争,则它们是最后选择的,因为它们被认为不如接收相同参数类型的方法具体。
要验证其编译时性质,您可以执行以下测试。
声明一个这样的类并编译它(即javac ChooseMethod.java
).
public class ChooseMethod {
public void doSomething(Number n){
System.out.println("Number");
}
}
声明第二个类,调用第一个类的方法并编译它(即javac MethodChooser.java
).
public class MethodChooser {
public static void main(String[] args) {
ChooseMethod m = new ChooseMethod();
m.doSomething(10);
}
}
如果您运行该程序(即java MethodChooser
),输出显示Number
.
现在,添加第二个更具体的超载方法到ChooseMethod
类,并重新编译它(但不要重新编译其他类)。
public void doSomething(Integer i) {
System.out.println("Integer");
}
如果再次运行 main,输出仍然是Number
.
基本上,因为它是在编译时决定的。如果你重新编译MethodChooser
类(带有 main 的类),然后再次运行程序,输出将是Integer
.
因此,如果要强制选择重载方法之一,参数的类型必须与编译时的参数类型相对应,而不仅仅是运行时的参数类型。
在运行时覆盖方法选择
同样,方法的签名是在编译时决定的,但实际的实现是在运行时决定的。
声明一个这样的类并编译它。
public class ChooseMethodA {
public void doSomething(Number n){
System.out.println("Number A");
}
}
然后声明第二个扩展类并编译:
public class ChooseMethodB extends ChooseMethodA { }
在 MethodChooser 类中,您执行以下操作:
public class MethodChooser {
public static void main(String[] args) {
ChooseMethodA m = new ChooseMethodB();
m.doSomething(10);
}
}
如果你运行它你会得到输出Number A
,这是好的,因为该方法尚未被重写ChooseMethodB
因此被调用的实现是ChooseMethodA
.
现在,添加一个重写的方法MethodChooserB
:
public void doSomething(Number n){
System.out.println("Number B");
}
重新编译这个,然后再次运行 main 方法。
现在,你得到输出Number B
因此,实现是在运行时选择的,而不是重新编译MethodChooser
需要上课。