我对Java的 varargs 方法感到有点困惑:
public static int sum(int ...a) {
return 0;
}
public static double sum(double ...a) {
return 0.0;
}
当我尝试在不传递任何参数的情况下调用sum()
时,调用了int
版本的方法。我不明白为什么;通常编译器必须引发错误。
相比之下,当我尝试在没有任何参数的情况下调用sum
时,以下代码会生成编译器错误:
public static int sum(int ...a) {
return 0;
}
public static boolean sum(boolean ...a) {
return true;
}
答案 0 :(得分:55)
这里适用的一般规则是:如果一个方法签名严格更具体,那么Java会选择它而不会出错。
如果您可以完全删除方法签名,并且另一个不太具体的方法签名适用于每个现有调用,则方法签名更具体。
如果在签名sum(int... args)
和sum(double... args)
之间进行选择,则签名sum(int... args)
更具体,因为该方法的任何调用也可以传递给sum(double... args)
通过应用扩大转换。同样不适用于sum(boolean... args)
方法,该方法无法进行类似转换。
Java语言规范,SE 8版本:
15.12。方法调用表达式
15.12.2.5. Choosing the Most Specific Method
Java编程语言使用选择最具体方法的规则。
...
对于带有参数表达式e1,...,ek的调用,如果满足以下任何条件,则一个适用的方法m1比另一个适用的方法m2更具体:
...
- m2不是通用的,m1和m2适用于严格或松散调用,其中m1具有形式参数类型S1,...,Sn和m2具有形式参数类型T1,...,Tn,类型对于所有i(1≤i≤n,n = k),对于参数ei,Si比
更具体。 ...
如果S<:T(§4.10),则类型S比任何表达式的类型T更具特异性。
4.10。子类型
4.10.1. Subtyping among Primitive Types
double> 1 float
浮动> 1长
long> 1 int
答案 1 :(得分:7)
如this answer中所述,在选择要使用的重载方法时会遵循规则。
引用:
- 原始扩展使用可能的最小方法参数
- 包装类型不能加宽到另一种包装类型
- 您可以从int到Integer并将其扩展为Object但不会为Long
- 加宽节拍拳击,拳击比赛。
- 你可以选择Box然后加宽(一个int可以通过整数成为对象)
- 你不能加宽然后Box(一个int不能变长)
- 你不能将var-args与两个加宽和拳击结合起来。
醇>
(让我们像这样重新定义规则1:“原始扩展使用最具体的方法参数。”)
因此,考虑到这些规则,我们可以了解这里发生了什么:
根据规则一,原始扩展使用尽可能最具体的方法参数。由于int
由非十进制数表示(例如1
),而double
由十进制数表示,精度比{{1}高32个字节(例如float
),我们可以说1.0
s是“小于”或“小于”int
s,按照这种逻辑,double
可以是“晋升”为int
和double
可以“降级”为double
。
简单地说,可以扩展到另一个原语的原语(例如int
- > int
- > float
)更具体另一个。例如,double
更具体而不是int
,因为double
可以提升为1
。
当你没有传递这些重载的同名vararg方法的参数时,由于返回实际上是相同的(分别为0和0.0),编译器会选择使用类型为{{vararg>的vararg的方法。 1}}因为更具体。
那么,当你介绍分别引入1.0
和int
s(不能相互扩展的类型)的相同方法时,编译器现在无法选择使用的方法因为int
s不能像boolean
s,int
和int
那样被“提升”或“降级”。因此,它会抛出编译错误。
我希望这可以帮助您了解正在发生的事情。