Java中方法重载的Varargs
以下代码无法编译。
package varargspkg; public class Main { public static void test(int... i) { for (int t = 0; t < i.length; t++) { System.out.println(i[t]); } System.out.println("int"); } public static void test(float... f) { for (int t = 0; t < f.length; t++) { System.out.println(f[t]); } System.out.println("float"); } public static void main(String[] args) { test(1, 2); //Compilation error here quoted as follows. } }
发出编译时错误。
参考测试是不明确的,varargspkg.Main中的方法测试(int …)和varargspkg中的方法测试(float …)匹配
这似乎是显而易见的,因为方法调用test(1, 2);
的参数值test(1, 2);
可以提升为int
以及float
如果任何一个或两个参数后缀为F
或f
,则编译。
但是,如果我们使用相应的包装器类型表示方法签名中的接收参数,如下所示
public static void test(Integer... i) { System.out.println("Integer" + Arrays.asList(i)); } public static void test(Float... f) { System.out.println("Float" + Arrays.asList(f)); }
然后调用方法test(1, 2);
不会发出任何编译错误。 在这种情况下要调用的方法是接受一个Integer
varargs参数(前面代码片段中的第一个)的方法。
为什么在这种情况下错误与第一种情况没有报告? 这里似乎都应用了自动装箱和自动类型提升。 是否首先应用自动装箱以便解决错误?
Oracle文档说,
一般来说,你不应该重载varargs方法,否则程序员很难弄清楚调用哪些重载。
这个链接的最后一句话。 然而,为了更好地理解varargs。
另外要添加下面的代码编译就好了。
public class OverLoading { public static void main(String[] args) { load(1); } public static void load(int i) { System.out.println("int"); } public static void load(float i) { System.out.println("float"); } }
编辑:
以下是指示编译错误的快照 。 我创建了一个新的应用程序,因此包名称不同。
我正在使用JDK 6。
你可以boxing and widening
或Box
但你不能同时做到这Widen
,除非你boxing and widening
到Object
(一个int到整数(拳击)然后整数到对象(加宽)是合法的,因为每个类都是Object
的子类,所以它可以将Integer
传递给Object
参数)
类似地,对Number
的int
也是合法的(int – > Integer – > Number)因为Number是Integer
的超类,所以它是可能的。
我们在你的例子中看到这个: –
public static void test(Integer...i) public static void test(Float...f)
选择要选择哪种重载方法,组合Boxing,Widening和Var-args时,遵循一些规则: –
- 原始扩展使用可能的
smallest
方法参数 - 包装类型不能扩展到另一种包装类型
- 您可以从int到Integer并将其扩展为Object而不是Long
- 加宽节拍拳击,拳击节拍Var-args。
- 你可以Box然后加宽(一个
int
可以通过Integer
成为Object
) - 你不能加宽然后Box(一个
int
不能变Long
) - 你不能将var-args与拓宽或装箱结合起来
因此,根据上述规则: –
当您将两个整数传递给上述函数时,
- 根据规则3,它必须首先
Widened
,然后Boxed
以适合Long
,根据规则5是非法的(你不能加宽然后盒子)。 - 因此,它被Boxed存储在
Integer
var-args中。
但在第一种情况下,你有使用var-args
原始类型的方法: –
public static void test(int...i) public static void test(float...f)
然后test(1, 2)
可以调用这两种方法(因为它们都不适合rule 1
适用): –
- 在第一种情况下,它将是
var-args
- 在第二种情况下,它将是加宽,然后是Var-args(允许)
现在,当你有一个只有一个int和一个flost的方法时: –
public static void test(int i) public static void test(float f)
然后在使用test(1)
调用时,遵循规则1,并且选择最小可能的加宽(即,根本不需要加宽的int
)。 因此将调用第一个方法。
有关更多信息,请参阅JLS - Method Invocation Conversion
在Java中, 1
表示如何表示int
。 它既可以自动装入Integer
实例,也可以提升为float
,这就解释了为什么编译器无法决定它应该调用的方法。 但它永远不会被自动装箱为Long
或Float
(或任何其他类型)。
另一方面,如果你写1F
,它是float
的表示,可以自动装箱到Float
(并且,同样的精神,永远不会自动装箱到Integer
或其他任何东西)。
为什么在这种情况下错误与第一种情况没有报告? 这里似乎都应用了自动装箱和自动类型提升。 是否首先应用自动装箱错误?
只是一个意见 – 在varargs的情况下,JVM实际上必须创建一个参数数组。 在Integer和Float的情况下,很明显它应该创建什么类型的数组。 因此,它可能是没有歧义错误的原因。
但是,它有点令人困惑,为什么它不能创建一个整数数组,默认情况下1,3是整数。
在过去的varargs和重载错误中,这里已经讨论过这个了吗? 根据讨论,这实际上是一个错误 。
在Java 6中,问题出在您的genericsinstantiation
之前,然后找到可以调用的方法。
When you write 1,2 -> it can be be both int[] or float[] and hence the issue being complained. When you write 1,2F -> it can be be only float[] and hence the NO issue being complained.
与其他两个选项相同,即
When you write 1F,2 -> it can be be only float[] and hence the NO issue being complained. When you write 1F,2F -> it can be be only float[] and hence the NO issue being complained.
另一方面,当您使用int
或float
,没有变量类型实例化。 当你使用1
,它首先尝试使用int
作为参数来查找方法,否则,它会提升类型并使用float标识方法。 如果两种方法都可用,则首先使用带有int
方法。
Java 7中不会出现歧义问题,因为它可以更好地处理数据类型检查和升级。