代码
public class TestOverload {
public TestOverload(int i){System.out.println("Int");}
public TestOverload(char... c){System.out.println("char");}
public static void main(String[] args) {
new TestOverload('a');
new TestOverload(65);
}
}
输出
Int
Int
这是预期的行为吗?如果是这样,为什么呢?我期待:char,Int
注意:我使用的是Java 8
答案 0 :(得分:81)
当编译器确定要选择哪种重载方法时,使用varargs(...
)的方法具有最低优先级。因此,当您使用单个TestOverload(int i)
参数TestOverload(char... c)
致电TestOverload
时,会char
选择'a'
,因为char
可以自动提升为int
TestOverload(char... c)
。
第一阶段(§15.12.2.2)执行重载解析而不允许装箱或拆箱转换,或使用变量arity 方法调用。如果在此阶段没有找到适用的方法 然后处理继续到第二阶段。 这保证了在Java编程中有效的任何调用 Java SE 5.0之前的语言不会被视为不明确的结果 引入变量arity方法,隐式装箱和/或 拆箱。但是,变量arity方法的声明(§8.4.1) 可以更改为给定方法方法调用选择的方法 表达式,因为变量arity方法被视为固定的 第一阶段的arity方法。例如,声明m(对象...) 在已经声明m(Object)的类中导致m(Object)为no 为某些调用表达式(例如m(null))选择更长的时间,如 m(Object [])更具体。
第二阶段(§15.12.2.3)执行重载解析,同时允许装箱和拆箱,但仍然排除使用变量arity方法调用。如果在此期间未找到适用的方法 阶段然后处理继续到第三阶段。这确保了永远不会通过变量arity选择方法 方法调用,如果它适用于固定arity方法 调用
- 醇>
第三阶段(§15.12.2.4)允许重载与变量arity方法,装箱和拆箱相结合。
编辑:
你希望强制编译器调用char[]
构造函数,你可以传递给构造函数调用new TestOverload (new char[] {'a'});
:
{{1}}
答案 1 :(得分:36)
是的,这是预期的行为。方法调用的优先顺序如下:
以下是与Java docs相关的摘录: -
确定适用性的过程首先要确定可能适用的方法(§15.12.2.1)。
该过程的其余部分分为三个阶段,以确保与Java SE 5.0之前的Java编程语言版本兼容。阶段是:
第一阶段(§15.12.2.2)执行重载解析而不允许装箱或拆箱转换,或使用变量arity方法调用。如果在此阶段没有找到适用的方法,则处理继续到第二阶段。
这保证了在Java SE 5.0之前在Java编程语言中有效的任何调用都不会因为引入变量arity方法,隐式装箱和/或取消装箱而被认为是不明确的。但是,变量arity方法(第8.4.1节)的声明可以更改为给定方法方法调用表达式选择的方法,因为变量arity方法在第一阶段被视为固定arity方法。例如,在已声明m(Object)的类中声明m(Object ...)会导致不再为某些调用表达式(例如m(null))选择m(Object),因为m(Object [] )更具体。
第二阶段(§15.12.2.3)在允许装箱和拆箱的同时执行重载解析,但仍然排除使用变量arity方法调用。如果在此阶段没有找到适用的方法,则处理继续到第三阶段。
这确保了如果通过固定的arity方法调用适用,则永远不会通过变量arity方法调用选择方法。
第三阶段(§15.12.2.4)允许重载与变量arity方法,装箱和拆箱相结合。
答案 2 :(得分:13)
Joshua Bloch(Effective Java,2nd Ed)的坚实建议:
"只选择具有重复方法的参数作为具有-radically-different类型的参数。"
具有完全不同类型的对象是无法合理地转换为其他参数类型的对象。遵循这条规则可以节省数小时调试一个神秘的错误,当编译器在编译时选择你没想到的方法重载时会发生这种错误。
您的代码行违反了此规则并为错误打开了大门:
public TestOverload(int i){System.out.println("Int");}
public TestOverload(char... c){System.out.println("char");}
char
可以与int
互换,因此您可以预测调用将会发生什么的唯一方法是转到Java语言规范并阅读有关过载如何过度的一些神秘规则解决。
幸运的是,这种情况不应该需要JLS研究。如果你的参数彼此没有根本的不同,那么最好的选择是不要重载。为方法赋予不同的名称,以便任何可能需要维护代码的人都不会出错或混淆。
时间就是金钱。
答案 3 :(得分:0)
我从this link获取代码并修改了部分内容:
public static void main(String[] args) {
Byte i = 5;
byte k = 5;
aMethod(i, k);
}
//method 1
static void aMethod(byte i, Byte k) {
System.out.println("Inside 1");
}
//method 2
static void aMethod(byte i, int k) {
System.out.println("Inside 2");
}
//method 3
static void aMethod(Byte i, Byte k) {
System.out.println("Inside 3 ");
}
//method 4
static void aMethod(Byte i, Byte ... k) {
System.out.println("Inside 4 ");
}
编译器为方法1,2和3提供错误(方法对于类型重载是不明确的)但不是4(为什么?)
答案在于java用于将方法调用与方法签名匹配的机制。该机制分三个阶段完成,如果找到匹配方法,则在每个阶段停止:
+第一阶段:使用拓宽找到匹配方法(找不到匹配方法)
+第二阶段:(也)使用装箱/拆箱找到匹配方法(方法1,2和3匹配)
+第三阶段:(也)使用var args(方法4匹配!)