我知道在 Java 7 中使用泛型类型的可变参数时会出现这种情况;
但我的问题是..
Eclipse 说“它的使用可能会污染堆”到底是什么意思?
还有
新的
@SafeVarargs
注释如何防止这种情况发生?
堆污染是一个技术术语。它引用的类型不是它们所指向的对象的超类型。
List<A> listOfAs = new ArrayList<>();
List<B> listOfBs = (List<B>)(Object)listOfAs; // points to a list of As
这可能会导致“无法解释”
ClassCastException
s。
// if the heap never gets polluted, this should never throw a CCE
B b = listOfBs.get(0);
@SafeVarargs
根本无法阻止这种情况。然而,有些方法可以证明不会污染堆,但编译器无法证明这一点。以前,此类 API 的调用者会收到恼人的警告,这些警告完全毫无意义,但必须在每个调用站点进行抑制。现在 API 作者可以在声明站点将其抑制一次。
但是,如果该方法实际上不安全,则将不再警告用户。
当你声明时
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);
如果您检查了该方法以确保它不包含此类漏洞,那么您可以使用
@SafeVarargs
对其进行注释以抑制警告。对于接口,请使用 @SuppressWarnings("unchecked")
。
如果您收到此错误消息:
Varargs 方法可能会因不可具体化的 varargs 参数而导致堆污染
并且您确定您的使用是安全的,那么您应该使用
@SuppressWarnings("varargs")
来代替。有关第二种错误的详细解释,请参阅 @SafeVarargs 是否适合此方法? 和 https://stackoverflow.com/a/14252221/14731。
参考资料:
@SafeVarargs
并不能阻止这种情况的发生,但它要求编译器在编译使用它的代码时更加严格。
http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html更详细地解释了这一点。
堆污染是指在通用接口上执行操作时得到ClassCastException
,并且它包含声明以外的其他类型。
Object[]
来保存参数。由于逃逸分析,JIT 可以优化此数组创建。 (我发现它是少数几次)它不能保证被优化掉,但我不会担心它,除非你发现它是你的内存分析器中的问题。
AFAIK
@SafeVarargs
抑制编译器发出警告,并且不会改变 JIT 的行为方式。
... ,也可以使用 List[] 非可变参数类型来调用。< A >
这是一个例子:
public static void testCode(){
List[] b = new List[1];
test(b);
}
@SafeVarargs
public static void test(List<A>... a){
}
如您所见,List[] b 可以包含任何类型的使用者,但此代码可以编译。如果您使用可变参数,那么没问题,但如果您在类型擦除后使用方法定义 - void test(List[]) - 那么编译器将不会检查模板参数类型。 @SafeVarargs 将抑制此警告。
@SafeVarargs
注释是相当安全的。您必须确保仅将声明的泛型类型的实例传递给该方法。如果该方法作为库公开在外部,则很难捕获此类错误。在这种情况下,最好避免此注释并使用集合类型(例如
Collection<Type1<Type2>>
)输入而不是可变参数(
Type1<Type2>...
)重写解决方案。就命名而言,“堆污染”现象在我看来是相当具有误导性的。在
文档中,甚至没有提到实际的 JVM 堆。软件工程中有一个问题,其中包含一些关于这种现象的命名的有趣想法。