使用可变参数可以在编译时放松类型检查吗?

use varargs can loose type-checking in compile time?

Effective Java 书中,第 42 条讨论了可变参数方法。

它说:

ReturnType1 suspect1(Object... args){}
<T> ReturnType2 suspect2(T... args){}

Methods with either of these signatures will accept any parameter list. Any compile-time type-checking that you had prior to the retrofit will be lost.

我很困惑。

问题一: 为什么 retrofit/refactor 可变参数的方法可以放松之前在方法中进行的类型检查?上面两个方法签名中,ObjectT 不是那里指定的类型吗?以何种方式我们可以准确地放松类型检查?书中解释说,参考了作者做的一个例子,但我不明白:

=======作者用来说服读者的例子=======

作者做了一个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) returns 成为数组 List<int[]> 的单元素数组。

问题二: 我理解这个例子,它确实是一个松散类型检查的例子,但是将原始 int array(digits) 包装到对象的操作是由 Arrays.asList() 方法进行的,而不是 vargars 用法(或者我在这里错了吗?),为什么作者使用这个例子来说明具有这两个签名的所有方法都可以在编译期间放松类型检查?如何?有什么例子可以说服我吗?

In above two method singature, isn't Object and T the type specified there?

有点,但不是真的。 T 将在运行时被擦除。一个简单的例子是 Arrays.asList(1, 2L, "3"),它包含 IntegerLongString。这导致 T 的编译时类型变为 <Serializable & Comparable>(这是所有这 3 个 类 的超类型)。因此,根据您传递的内容,T "adapts" 在最广泛的情况下变为 Object

..the action of wrapping primitive int array(digits) to an object is conducted by Arrays.asList() method, not the vargars usage (or am I wrong here?)

Arrays.asList() 只会将输入数组中的每个元素分配给一个列表。由于可变参数,int[] {1, 2, 3} 将是一个元素(因此 T 变为 int[] 因为 int 不是对象,而不是 Integer {1, 2, 3} 其中 T 变成 Integer)。当然可以编写代码,以便它检查是否有单个输入元素,然后检查它是否是一个数组,然后将 int[] 转换为 List<Integer>,但这会破坏泛型(int[] 突然变成 Integer),这不是它唯一的问题。

ReturnType1 suspect1(Object... args){}

由于所有对象都继承自Object class,因此编译器执行编译时类型检查是无用的。

<T> ReturnType2 suspect2(T... args){}

同样在这里,变量类型T可能是你在运行时想要的任何东西,因此它对编译器检查类型没有用。

要知道如果使用 vararg 会丢失类型检查,请使用 vararg 编写几行代码并故意将不允许的类型作为 vararg 传递:您将看到编译器是否失败或是否会在运行时抛出异常.

这是一个类型系统主题。

可以将 new Object[]{1, 2L, "3"} 分配给 Object[],也可以将 new Integer[]{1, 2, 3} 分配给 Object[],因为 数组在 Java。无法将 int[] 分配给 Object[],但可以将 int[] 分配给 Object... args

采用 Object[] 作为参数的方法将接受任何类型的对象数组,在 Java 1.5 之前或之后。但是以Object... args为参数的方法(仅在Java 1.5之后)也可以接受int[].