Java SafeVarargs注释,是否存在标准或最佳实践?

我最近遇到了java @SafeVarargs注释。 在Java中使用可变参数函数的不安全让我相当困惑(堆中毒擦除types?),所以我想知道一些事情:

  1. 是什么使得可变的Java函数在@SafeVarargs意义上是不安全的(最好以深入的例子的forms来解释)?

  2. 为什么这个注释留给程序员呢? 这不是编译器应该能够检查的东西吗?

  3. 是否有一些标准必须坚持,以确保他的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[] { ... } (这里ObjectT的上界;如果上界有些不同的话,那就是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)上面提到的区别太复杂了,不易自动区分。