我在两个超载问题就在最近,我无法找到一个答案,并没有java环境运行一些测试代码。 我希望有人能够通过装配的所有的Java编译遵循遵循超载或交替在已经存在的列表指着我的规则列表帮助我。
首先,当两种方法最终的区别仅在于可变参数的参数,在做好每被调用什么情况下,你可以调用可变参数的方法,没有ARGS?
private void f(int a) { /* ... */ }
private void f(int a, int... b) { /* ... */ }
f(12); // calls the former? I would expect it to
f(12, (int[])null); // calls latter, but passes null for b?
// Can I force the compiler to call the second method in the same fashion
// as would happen if the first method didn't exist?
第二个问题,当两种方法从一个继承类型不同的另一个都会调用? 我期望最派生版本被称为,和铸造不准叫对方。
interface A {}
class B implements A {}
class C implements A {}
private void f(A a) {}
private void f(B b) {}
f(new C()); // calls the first method
f(new B()); // calls the second method?
f((A)(new B()); // calls the first method using a B object?
这是两个例子,但作为一个代码阅读器我喜欢的用于解决这一确切排序的规则规范列表,我经常没有时间设置一个构建环境检查什么编译器在做什么。
重载VS重写
正确的实现方法的选择是在运行时完成正如你指出的那样,现在要调用的方法的签名是在编译时决定。
重载方法选择在编译时
在Java语言规范 (JLS)在第15.12 方法调用表达式详细解释,编译器如下选择来调用正确的方法的过程。
在那里,你会发现这是一个编译时的任务。 该JLS说,在第15.12.2:
该步骤使用的方法和类型的参数表达式的 名称 ,以定位都是可访问的和适用的可以存在多于一种这样的方法,在这种情况下选择最具体的一个方法。
通常情况下,可变参数的方法是最后的选择,如果他们与其他候选方法竞争,因为他们被认为比接受相同的参数类型的那些不太具体。
为了验证这种编译时的性质,可以做如下测试。
声明一个类像这样和编译。
public class ChooseMethod {
public void doSomething(Number n){
System.out.println("Number");
}
}
声明调用的第一个的方法的第二类并编译它。
public class MethodChooser {
public static void main(String[] args) {
ChooseMethod m = new ChooseMethod();
m.doSomething(10);
}
}
如果调用为主,输出表示Number
。
现在,更具体的重载方法添加第二个到ChooseMethod
类,并重新编译它(但不要重新编译其他类)。
public void doSomething(Integer i) {
System.out.println("Integer");
}
如果再次运行为主,输出仍然Number
。
基本上,因为它是在编译时决定。 如果你重新编译MethodChooser
类(一个与主),并再次运行该程序,输出将是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");
}
并重新编译仅这一项,并再次运行的主要方法。
现在,你得到的输出Number B
这样,执行被选择在运行时,而不是重新编译MethodChooser
物所需的类。
第一个问题:
你的假设是正确的。 到第二呼叫f()
将调用可变参数的方法。 你可以让编译器调用第二个方法有:
private void f(int a) {
f(a, null);
}
第二个问题:
是。 但是,你不能扩展接口。 如果更改A
到一个抽象类,事情将会编译。