瓦拉格斯堆积污染:有什么大不了的?

我正在阅读有关varargs堆污染的文章,我真的不明白varargs或不可再生类型如何对没有通用性就不存在的问题负责。事实上,我可以很容易地更换

public static void faultyMethod(List<String>... l) {
    Object[] objectArray = l; // Valid
    objectArray[0] = Arrays.asList(42);
    String s = l[0].get(0); // ClassCastException thrown here
}

public static void faultyMethod(String... l) {
    Object[] objectArray = l; // Valid
    objectArray[0] = 42;  // ArrayStoreException thrown here
    String s = l[0];
}

第二个只是使用数组的协方差,这确实是这里的问题。(即使是可重用的,我想它仍然是 的子类,我仍然能够将任何对象分配给数组。当然,我可以看到两者之间有一点区别,但是无论是否使用泛型,这段代码都是错误的。List<String>Object

他们所说的堆污染是什么意思(这让我想到了内存使用情况,但他们谈论的唯一问题是潜在的类型不安全性),它与使用数组协方差的任何类型违规有何不同?


答案 1

你是对的,常见的(和基本的)问题是数组的协方差。但是在你给出的这两个例子中,第一个更危险,因为可以修改你的数据结构,并将它们置于稍后会中断的状态。

请考虑一下,如果您的第一个示例没有触发 ClassCastException:

public static void faultyMethod(List<String>... l) {
  Object[] objectArray = l;           // Valid
  objectArray[0] = Arrays.asList(42); // Also valid
}

以下是某人如何使用它:

List<String> firstList = Arrays.asList("hello", "world");
List<String> secondList = Arrays.asList("hello", "dolly");
faultyMethod(firstList, secondList);
return secondList.isEmpty()
  ? firstList
  : secondList;

所以现在我们有一个实际上包含一个,它安全地漂浮在周围。在某个时间点之后 - 可能要晚得多,如果它被序列化,可能晚并且在另一个JVM中 - 最终会有人执行。这种故障与导致它的原因相去甚远,以至于可能很难追踪。List<String>IntegerString s = theList.get(0)

请注意,ClassCastException的堆栈跟踪并没有告诉我们错误真正发生在哪里;它只是告诉我们是谁触发了它。换句话说,它没有给我们太多关于如何修复错误的信息;这就是使它比ArrayStoreException更大的原因。


答案 2

数组和 List 之间的区别在于数组检查其引用。例如:

Object[] array = new String[1];
array[0] = new Integer(1); // fails at runtime.

然而

List list = new ArrayList<String>();
list.add(new Integer(1)); // doesn't fail.