在Effective Java书中,第42项讨论了varargs方法。
它说:
ReturnType1 suspect1(Object... args){}
<T> ReturnType2 suspect2(T... args){}
具有这些签名之一的方法将接受任何参数 清单。您之前进行的所有编译时类型检查 改装将丢失。
我很困惑。
问题一:
为什么对varargs进行改型/重构方法可能会使以前的类型检查松散?在以上两种方法中,Object
和T
的类型不是在那里指定的吗?我们可以通过哪种方式完全松开类型检查?该书通过引用作者做出的一个示例进行了解释,但我不明白:
========示例作者用来说服读者=======
作者以Arrays.asList(...)
为例,说明在Java 1.5之前,以下代码会引发类型检查的编译时错误:
int[] digits = {3,1,4}
// Compiler error: asList(Object[]) in Arrays can't be applied to (int[])
System.out.println(Arrays.asList(digits));
并且由于 java1.5及更高版本,由于引入了varargs
,因此上述代码对于编译器来说是可以的。 Arrays.asList(digits)
将整个int[]
包装到一个对象,以便Arrays.asList(digits)
返回数组List<int[]>
的一个元素数组。
问题二:
我理解这个示例,它确实是一个丢失类型检查的示例,但是将原始int数组(digits
)包装到对象的操作是通过Arrays.asList()
方法执行的,而不是通过{{1} }用法(或者我在这里错了吗?),为什么作者使用此示例来说明带有这两个签名的所有方法在编译器期间可能会松动类型检查?怎么样?有什么说服我的例子吗?
答案 0 :(得分:1)
在以上两种方法中,不是Object和
T
指定的类型 在那里?
排序,但不是真的。 T
将在运行时删除。一个简单的示例是Arrays.asList(1, 2L, "3")
,其中包含Integer
,Long
和String
。这导致T
的编译时类型变为<Serializable & Comparable>
(这是所有这三个类的超类型)。因此,根据您通过的内容,T
的“适应性”在最广泛的情况下会变成Object
。
..将原始int数组(数字)包装到对象的操作是 由Arrays.asList()方法执行,而不是由vargars使用(或者我是 这里错了吗?)
Arrays.asList()
只会将输入数组中的每个元素分配给一个列表。由于存在可变参数,int[] {1, 2, 3}
将是一个元素(由于T
不是对象,因此int[]
成为int
,而{{1} }}变成Integer {1, 2, 3}
)。当然,可以编写代码,以便检查是否有单个输入元素,然后检查是否是数组,然后将T
转换为Integer
,但这会破坏泛型({{1} }突然变成int[]
),而这并不是唯一的问题。
答案 1 :(得分:0)
ReturnType1 suspect1(Object... args){}
由于所有对象都继承自Object
类,因此它对于编译器执行编译时类型检查没有用。
<T> ReturnType2 suspect2(T... args){}
在这里,变量类型T
可能是运行时所需的所有内容,因此它对于编译器检查类型毫无用处。
要知道如果使用vararg会丢失类型检查,请使用vararg编写几行代码,并故意将不允许的类型作为vararg传递:您将看到编译器是否失败o如果在运行时会引发异常。 / p>
答案 2 :(得分:0)
这是类型系统主题。
可以将new Integer[]{1, 2, 3}
分配给Object[]
,也可以将int[]
分配给Object[]
,因为数组在Java中是协变的 。无法将int[]
分配给Object... args
,但是可以将Object[]
分配给Object... args
。
以int[]
作为参数的方法将接受Java 1.5之前或之后的任何类型的对象数组。但是将{{1}}作为参数的方法(仅在Java 1.5之后)也可以接受{{1}}。