使用varargs重载function
这不会编译:
public class Methods { public static void method(Integer... i) { System.out.print("A"); } public static void method(int... i) { System.out.print("B"); } public static void main(String args[]) { method(7); } }
这将编译和工作:
public class Methods { public static void method(Integer i) { System.out.print("A"); } public static void method(int i) { System.out.print("B"); } public static void main(String args[]) { method(7); } }
第一个和第二个例子非常相似。 首先使用varargs,其次不使用varargs。 为什么一个有效,第二个没有。 7是原始的,因此在两种情况下都应该调用第二种方法。 这是正常的行为吗?
我发现了: 错误报告 堆栈溢出
这是对正在发生的事情的高级非正式总结。
首先,varargs语法实际上只是用于传递数组的语法糖。 因此method(7)
实际上将传递一个……的数组。
但是一阵什么? 这里有两个选项对应于方法的两个重载; 即int[]
或Integer[]
。
如果有两个或更多可能有效的重载(即正确的方法名称,正确的参数数量,可转换值),那么解析过程将选择与需要转换的匹配完全匹配的重载,并且如果只有候选者则会抱怨要求转换。 (这是对规则的大幅简化……有关完整的故事,请参阅JLS 第15.12节 ……并做好长时间/难以阅读的准备!)
所以你的第一个例子中发生的事情是,它试图在两种方法之间做出决定,这两种方法都需要转换; ie int
to int[]
与int
to Integer[]
。 基本上它无法决定使用哪种替代方案。 因此编译错误表明调用是模糊的。
如果你将varargs调用更改为通过显式Integer[]
或int[]
的调用,那么现在可以获得与两个重载中的一个完全匹配…并且上面的规则说这不是模棱两可的。
我理解为:7是原始的,所以它应该转换为array –
int[]
。
问题是7
也可以通过首先自动装箱int
转换为Integer[]
….
必须在数组中传递多个参数,但varargs
隐藏进程。 在上面的varargs
方法中,参数充当带引用名称的int
数组。 因此,如果您将其更改为:
public static void main(String args[]) { int[] s = {7}; method(s); }
第一堂课将编译并正常工作。
答案很难。 但它在JLS§15.12中有所描述。 方法调用表达式 。 在这些规范中,“ 变量arity ”方法是具有可变数量的参数的方法,因此varargs。
§15.12.2.4。 阶段3:确定适用的变量Arity方法
当且仅当满足以下所有条件时,方法m是适用的变量方法:
对于1≤i
i ,a i的类型可以通过方法调用转换为S i来转换。 如果k≥n,则对于n≤i≤k,可以通过方法调用转换将e i ,A i的类型转换为S n的分量类型。
如果k!= n,或者如果k = n并且An不能通过方法调用转换转换为S n [],则可以在调用点访问S n的擦除类型(第4.6节)。
如果m是如上所述的通用方法,那么U l
<:
B l [R 1 = U 1 ...,R p = U p ](1≤l≤p)。如果未找到适用的变量arity方法,则会发生编译时错误。
否则, 在适用的变量方法中选择最具体的方法(第15.12.2.5节 )。
因此,我们应该进一步研究§15.12.2.5 。
§15.12.2.5。 选择最具体的方法
一个名为
m
变量arity成员方法比另一个具有相同名称的变量arity成员方法更具体:
一个成员方法有n个参数,另一个有k个参数,其中n≥k,和:
第一成员方法的参数类型是T 1 ,...,T n-1 ,T n []。
另一种方法的参数类型是U 1 ,...,U k-1 ,Uk []。
如果第二种方法是通用的,则让R 1 ... R p (p≥1)为其类型参数,设B l为R l的声明界(1≤l≤p),设A 1 ... A p是在初始约束T i << U i (1≤i≤k-1)和T i << U k (k≤i≤n)下对此调用推断的类型参数(第15.12.2.7节),以及令S i = U i [R 1 = A 1 ,...,R p = A p ](1≤i≤k)。
否则,令S i = U i (1≤i≤k)。
对于从1到k-1的所有j,T j
<:
S j ,和,对于从k到n的所有j,T j
<:
S k ,和,如果第二种方法是如上所述的通用方法,那么
A l<:
B l [R 1 = A 1 ,...,R p = A p ](1≤l≤p)。
( T <: S
表示T是S的子类型 )
您的方法与这些条件不匹配,因此没有“ 最具体 ”方法。 所以,它说了一点:
可能没有方法是最具体的,因为有两种或更多种方法是最具体的。 在这种情况下:
如果所有最大特定方法都具有覆盖等效(§8.4.2)签名,则:
如果其中一个最大特定方法未被声明为abstract,则它是最具体的方法。
否则,如果所有最大特定方法都被声明为抽象,并且所有最大特定方法的签名具有相同的擦除(第4.6节),那么在具有最大特定方法的子集中任意选择最具体的方法。最具体的退货类型。
但是,当且仅当在每个最大特定方法的throws子句中声明了该exception或其擦除时,才会考虑使用最具体的方法抛出已检查的exception。
否则,我们说方法调用是不明确的,并且发生编译时错误。
所以,结论是:你的方法在JLS之后是模糊的。