1) Internet 和 StackOverflow 上有很多关于泛型和可变参数的特定问题的示例。基本上,它是当您拥有可变数量的类型参数类型的参数时:
<T> void foo(T... args);
在 Java 中,可变参数是一种语法糖,在编译时会进行简单的“重写”:X... 类型的可变参数参数被转换为 X[] 类型的参数;每次调用此 varargs 方法时,编译器都会收集 varargs 参数中的所有“可变参数”,并创建一个类似于 new X[] { ...(arguments go here)... } 的数组。
当 varargs 类型像 String... 这样具体时,这很有效。当它是像 T... 这样的类型变量时,它也适用于已知 T 是该调用的具体类型时。例如如果上面的方法是 Foo<T> 类的一部分,并且你有一个 Foo<String> 引用,那么在它上面调用 foo 就可以了,因为我们知道 T 在代码中的那个点是 String。
但是,当T 的“值”是另一个类型参数时,它不起作用。在 Java 中,不可能创建类型参数组件类型 (new T[] { ... }) 的数组。所以Java改用new Object[] { ... }(这里Object是T的上限;如果有不同的上限,那就是Object而不是Object),然后给你一个编译器警告。
那么创建new Object[] 而不是new T[] 或其他什么有什么问题?好吧,Java 中的数组在运行时知道它们的组件类型。因此,传递的数组对象在运行时将具有错误的组件类型。
对于可变参数的最常见用法,简单地迭代元素,这没有问题(你不关心数组的运行时类型),所以这是安全的:
@SafeVarargs
final <T> void foo(T... args) {
for (T x : args) {
// do stuff with x
}
}
但是,对于任何依赖于传递数组的运行时组件类型的东西,它都是不安全的。这是一个不安全和崩溃的简单示例:
class UnSafeVarargs
{
static <T> T[] asArray(T... args) {
return args;
}
static <T> T[] arrayOfTwo(T a, T b) {
return asArray(a, b);
}
public static void main(String[] args) {
String[] bar = arrayOfTwo("hi", "mom");
}
}
这里的问题是我们依赖args 的类型为T[] 以便将其返回为T[]。但实际上运行时参数的类型并不是T[]的实例。
3) 如果您的方法有一个T... 类型的参数(其中T 是任何类型参数),那么:
- 安全:如果您的方法仅依赖于数组元素是
T 的实例这一事实
- 不安全:如果它取决于数组是
T[] 的实例这一事实
依赖于数组的运行时类型的事情包括:将它作为T[]类型返回,将它作为参数传递给T[]类型的参数,使用.getClass()获取数组类型,将它传递给方法这取决于数组的运行时类型,如List.toArray() 和Arrays.copyOf() 等。
2)我上面提到的区分太复杂了,不容易自动区分。