Java SafeVarargs注释,是否存在标准或最佳实践?
我最近遇到了java @SafeVarargs注释。 在Java中使用可变参数函数的不安全让我相当困惑(堆中毒擦除types?),所以我想知道一些事情:
-
是什么使得可变的Java函数在@SafeVarargs意义上是不安全的(最好以深入的例子的forms来解释)?
-
为什么这个注释留给程序员呢? 这不是编译器应该能够检查的东西吗?
-
是否有一些标准必须坚持,以确保他的function确实是安全的? 如果没有,最好的做法是什么来确保它?
1)在互联网和StackOverflow上有很多关于generics和可变参数的特殊问题的例子。 基本上,当你有一个types参数types的可变数量的参数:
void foo(T... args);
在Java中,可变参数是一个语法糖,它在编译时经历一个简单的“重写”:一个types为X...
的可变参数被转换为X[]
types的参数。 并且每次调用这个varargs方法,编译器都会收集varargs参数中的所有“variables参数”,并像new X[] { ...(arguments go here)... }
一样创build一个数组new X[] { ...(arguments go here)... }
。
这可以很好地工作,当可变参数types是具体的像String...
当它是一个像T...
这样的typesvariables时,当T
被称为该调用的具体types时,它也可以工作。 例如,如果上面的方法是类Foo<T>
,并且您有一个Foo<String>
引用,那么调用foo
就可以了,因为我们知道T
是代码中的那个点的String
。
但是,当T
的“值”是另一个types参数时,它不起作用。 在Java中,不可能创build一个types参数组件types的数组( new T[] { ... }
)。 所以Java使用new Object[] { ... }
(这里Object
是T
的上界;如果上界有些不同的话,那就是Object
而不是Object
),然后给你一个编译警告。
那么创buildnew Object[]
而不是new T[]
或其他什么是错的? 那么,Java中的数组在运行时就知道它们的组件types。 因此,传递的数组对象在运行时将具有错误的组件types。
对于可变参数最常见的用法,只需遍历元素,这不是问题(你不关心数组的运行时types),所以这是安全的:
@SafeVarargs final void foo(T... args) { for (T x : args) { // do stuff with x } }
但是,对于依赖于传递数组的运行时组件types的任何事情来说,这将是不安全的。 这是一个不安全和崩溃的简单例子:
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
的types为T[]
,以便将其返回为T[]
。 但实际上运行时参数的types不是T[]
一个实例。
3)如果你的方法有一个T...
types的参数(其中T是任何types参数),那么:
- 安全:如果你的方法只取决于数组元素是
T
实例 - 不安全:如果这取决于数组是
T[]
一个实例
依赖于数组的运行时types的东西包括:将它作为typesT[]
返回,将其作为parameter passing给T[]
types的参数,使用.getClass()
获取数组types,并将其传递给依赖于在数组的运行时types上,如List.toArray()
和Arrays.copyOf()
等
2)上面提到的区别太复杂了,不易自动区分。