下面的代码会产生语法错误:
我代码:对于C
类型,方法f(int[])
是不明确的
public class C{
public static void f(int... i)
{
System.out.println("a");
}
public static void f(Integer... i)
{
System.out.println("b");
}
public static void main(String[] args) {
f(new Integer(2));
}
}
如果我用数组符号[]
代替...
,并且用f(new Integer[]{3,4,5})
正确地调用函数,编译器可以正确地决定我要使用哪个方法。
编译器不能决定用...
调用哪个函数的原因是什么?
查找"正确"的过程;在JLS第15.12节"方法调用表达式"中解释了用于特定调用站点的方法。在进行基本的完整性检查之后,相关部分从15.12.2.1节,识别潜在的适用方法开始。在您的情况下,根据这个定义,两个方法都可能适用。
后续过程包括三个阶段。在您的例子中,该方法是一个变量方法(由于&;varargs")。因此,它立即从15.12.2.4开始,阶段3:确定适用的可变密度方法。
方法m是一个适用的变量方法,当且仅当满足以下所有条件:
- 对于1≤i
- …
(其他条件在这里不相关)
方法调用转换(JLS, 5.3)允许以下转换:
- 标识转换(§5.1.1)
- 一个扩展的原语转换(§5.1.2)
- 扩大参考转换(§5.1.5)
- 一个装箱转换(§5.1.7)可选地跟随扩展引用转换
- 一个可选的拆箱转换(§5.1.8),后跟一个扩展原语转换。
从Integer
到Integer
的转换是标识转换(第一个要点)。从Integer
到int
的转换是一个开箱转换(最后一个要点)。
所以这两种方法都是"适用变量方法"。
Varargs
和Boxing
都是在Java 5以后引入的。在Java 5中出现了处理遗留代码的问题,其中编译器执行扩展操作(例如,在有long的地方,编译器将选择long而不是Integer,因为在Java 5之前允许从int扩展到long的操作),但是由于varargs和Boxing处于同一级别(Java 5起),编译器无法确定正确的类型。只需删除变量并用数组替换,就可以了
当Varargs和装箱/拆箱组合使用时,它们之间没有定义的优先级
规则是WBV/WAV
在方法重载的情况下,拓宽->(自动)装箱-> Varargs。
-
扩大
int -> long -> float -> double
-
拳击
Integer -> Number -> Object
-
a)扩大然后Varargs
int... -> long... -> float... -> double...
b)将Varargs装箱
Integer... -> Number... -> Object...
换句话说
Varargs [Last option]
^
|
Boxing [Newer style, added in Java 1.5]
^
|
Widening [Older style, choosen by the compiler to support existing code]
注意:
-
Boxing
则Widening
也可以。 -
Varargs
则不允许Boxing
。 -
Varargs
被视为Widening
和boxing
之间的最后一个选项 - 选择
Widening then Varargs
或Boxing then Varargs
。如果这两种可能性都存在,则会导致歧义的方法调用。
在f(new Integer(2))
的情况下的转换。根据Boxing
规则,Widening
Integer -> Number -> Object -> int -> long -> float -> double
现在double
旁边有两种可能性
3.a)拉大Varargs (
int...
)3.b) Boxing then Varargs (
Integer...
)
但是两者都存在,因此导致歧义的方法调用。
f(new Integer[]{3,4,5})
Integer...
是直接匹配。Varargs
then Boxing
不允许
…用于可变数量的参数。你必须传递像f(1,2,3)这样的值,这就是错误的原因。