通过varargs参数潜在的堆污染

时间:2012-09-17 15:04:12

标签: java eclipse generics variadic-functions

我理解在使用带有泛型类型的varargs时,Java 7会出现这种情况;

但我的问题是......

当Eclipse说“它的使用可能会污染堆?”时,Eclipse究竟是什么意思?

并且

新的@SafeVarargs注释如何阻止这种情况?

6 个答案:

答案 0 :(得分:230)

堆污染是一个技术术语。它引用的引用的类型不是它们指向的对象的超类型。

List<A> listOfAs = new ArrayList<>();
List<B> listOfBs = (List<B>)(Object)listOfAs; // points to a list of As

这可能导致“无法解释的”ClassCastException

// if the heap never gets polluted, this should never throw a CCE
B b = listOfBs.get(0); 

@SafeVarargs根本不会阻止这种情况。但是,有些方法可以证明不会污染堆,编译器就是无法证明它。以前,这些API的调用者会得到令人讨厌的警告,这些警告完全没有意义,但必须在每个呼叫站点被抑制。现在API作者可以在声明站点禁止它一次。

但是,如果该方法实际上安全,则不会再向用户发出警告。

答案 1 :(得分:214)

宣布

public static <T> void foo(List<T>... bar)编译器将其转换为

public static <T> void foo(List<T>[] bar)然后

public static <T> void foo(List[] bar)

然后出现危险,您将错误地将错误的值分配给列表,并且编译器不会触发任何错误。例如,如果TString,则以下代码将编译而不会出现错误,但会在运行时失败:

// 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);

如果您查看了该方法以确保其不包含此类漏洞,则可以使用@SafeVarargs对其进行注释以禁止显示警告。对于接口,请使用@SuppressWarnings("unchecked")

如果收到以下错误消息:

  

Varargs方法可能会导致来自不可恢复的varargs参数的堆污染

并且您确定您的使用是安全的,那么您应该使用@SuppressWarnings("varargs")。有关第二种错误的详细解释,请参阅Is @SafeVarargs an appropriate annotation for this method?https://stackoverflow.com/a/14252221/14731

参考文献:

答案 2 :(得分:7)

@SafeVarargs并不会阻止它发生,但它要求编译器在编译使用它的代码时更严格。

http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html更详细地解释了这一点。

堆污染是指在通用接口上执行操作时获得ClassCastException并且它包含的其他类型而不是声明的。

答案 3 :(得分:5)

使用varargs时,可能会导致创建Object[]来保存参数。

由于转义分析,JIT可以优化此阵列创建。 (我发现它的少数几次之一)它不能保证被优化掉,但除非你在内存分析器中看到它的问题,否则我不会担心它。

AFAIK @SafeVarargs禁止编译器发出警告,并且不会改变JIT的行为方式。

答案 4 :(得分:1)

答案 5 :(得分:0)