当你宣布 public static <T> void foo(List<T>... bar)编译器将其转换为 public static <T> void foo(List<T>[] bar)然后到 public static void foo(List[] bar) 这样一来,你就有可能错误地将不正确的值赋值到列表中,而编译器不会触发任何错误。例如,如果T是String,那么下面的代码将编译无误,但在运行时会失败:
// First, strip away the array type (arrays allow this kind of upcasting)
Object[] objectArray = bar;
// Next, insert an element with an incorrect type into the array
objectArray[0] = Arrays.asList(new Integer(42));
// Finally, try accessing the original array. A runtime error will occur
// (ClassCastException due to a casting from Integer to String)
T firstElement = bar[0].get(0);
The reason is because varargs give the option of being called with a non-parametrized object array. So if your type was List < A > ... , it can also be called with List[] non-varargs type. 下面是一个例子:
public static void testCode(){
List[] b = new List[1];
test(b);
}
@SafeVarargs
public static void test(List<A>... a){
}
6条答案
按热度按时间093gszye1#
堆污染是一个技术术语。它指的是引用的类型不是它们所指向对象的超类型。
这可能导致“无法解释”的
ClassCastException
。@SafeVarargs
完全不能防止这种情况。然而,有些方法可能不会污染堆,编译器只是无法证明这一点。以前,此类API的调用者会收到烦人的警告,这些警告完全没有意义,但必须在每个调用点都取消。现在API作者可以在声明点取消一次警告。但是,如果该方法实际上是 * 不 * 安全的,用户将不再受到警告。
ohtdti5x2#
当你宣布
public static <T> void foo(List<T>... bar)
编译器将其转换为public static <T> void foo(List<T>[] bar)
然后到public static void foo(List[] bar)
这样一来,你就有可能错误地将不正确的值赋值到列表中,而编译器不会触发任何错误。例如,如果
T
是String
,那么下面的代码将编译无误,但在运行时会失败:如果您检查了方法以确保它不包含这样的漏洞,那么您可以使用
@SafeVarargs
注解它以抑制警告。对于接口,使用@SuppressWarnings("unchecked")
。如果您收到此错误消息:
Varargs方法可能会由于不可定义的varargs参数而导致堆污染
并且您确信您的使用是安全的,那么您应该使用
@SuppressWarnings("varargs")
代替。请参见Is @SafeVarargs an appropriate annotation for this method?和https://stackoverflow.com/a/14252221/14731以获得第二种错误的详细解释。参考文献:
iibxawm43#
@SafeVarargs
不会阻止这种情况发生,但是它要求编译器在编译使用它的代码时更加严格。http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html将对此进行更详细的解释。
堆污染是指在泛型接口上执行操作时得到一个
ClassCastException
,并且它包含另一个未声明的类型。yzckvree4#
使用varargs时,可能会导致创建一个
Object[]
来保存参数。由于逃逸分析,JIT可以优化掉这个数组创建。(我发现它这样做的次数不多)它不能保证被优化掉,但我不会担心它,除非你在内存分析器中看到它是一个问题。
AFAIK
@SafeVarargs
禁止编译器发出警告,并且不更改JIT的行为方式。j8yoct9x5#
The reason is because varargs give the option of being called with a non-parametrized object array. So if your type was List < A > ... , it can also be called with List[] non-varargs type.
下面是一个例子:
正如你所看到的,List [] b可以包含任何类型的消费者,但是这段代码可以编译。如果你使用varargs,那么你就可以了,但是如果你在type-erasure-void测试(List [])之后使用方法定义-那么编译器将不会检查模板参数类型。@SafeVarargs将抑制这个警告。
s5a0g9ez6#
当你可以控制方法的调用方式时(例如类的私有方法),给方法添加
@SafeVarargs
注解是相当安全的。你必须确保只有声明的泛型类型的示例被传递给方法。如果方法作为库对外公开,就很难捕捉到这样的错误,在这种情况下,最好避免这种注解,用集合类型(例如
Collection<Type1<Type2>>
)输入而不是varargs(Type1<Type2>...
)重写解决方案。至于命名,在我看来,术语 * 堆污染 * 现象是相当误导的。在documentation中,实际的JVM * 堆 * 并没有被提及。在软件工程中有一个question包含了一些关于这种现象命名的有趣想法。