Dic*_*ici 18 java memory generics variadic-functions heap-pollution
我正在阅读有关varargs堆污染的内容,我并不真正了解varargs或non-reifiable类型将如何解决那些在没有通用性的情况下尚未存在的问题.的确,我可以很容易地取代
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
}
Run Code Online (Sandbox Code Playgroud)
同
public static void faultyMethod(String... l) {
Object[] objectArray = l; // Valid
objectArray[0] = 42; // ArrayStoreException thrown here
String s = l[0];
}
Run Code Online (Sandbox Code Playgroud)
第二个只是使用数组的协方差,这实际上是问题所在.(即使List<String>是可以恢复的,我想它仍然是子类,Object我仍然可以将任何对象分配给数组.)当然我可以看到两者之间有一点差别,但是这个代码是错误的,无论是是否使用泛型.
它们对堆污染的意义是什么(它让我考虑内存使用情况,但他们谈到的唯一问题是潜在的类型不安全),它与使用数组协方差的任何类型违规有何不同?
ysh*_*vit 13
你是对的,常见的(和基本的)问题是数组的协方差.但是在你给出的那两个例子中,第一个更危险,因为可以修改你的数据结构并将它们置于一个稍后会破坏的状态.
考虑您的第一个示例是否未触发ClassCastException:
public static void faultyMethod(List<String>... l) {
Object[] objectArray = l; // Valid
objectArray[0] = Arrays.asList(42); // Also valid
}
Run Code Online (Sandbox Code Playgroud)
以下是有人使用它的方式:
List<String> firstList = Arrays.asList("hello", "world");
List<String> secondList = Arrays.asList("hello", "dolly");
faultyMethod(firstList, secondList);
return secondList.isEmpty()
? firstList
: secondList;
Run Code Online (Sandbox Code Playgroud)
所以现在我们有一个List<String>实际上包含一个Integer,并且它安全地浮动.在某些时候 - 可能更晚,如果它被序列化,可能更晚以及在不同的JVM中 - 有人最终执行String s = theList.get(0).这种失败与导致它失败的原因相差甚远.
请注意,ClassCastException的堆栈跟踪不会告诉我们错误发生在哪里; 它只是告诉我们是谁触发了它.换句话说,它没有给我们提供有关如何修复bug的大量信息; 这就是使它成为一个比ArrayStoreException更大的交易.
数组和List之间的区别在于数组检查它的引用.例如
Object[] array = new String[1];
array[0] = new Integer(1); // fails at runtime.
Run Code Online (Sandbox Code Playgroud)
然而
List list = new ArrayList<String>();
list.add(new Integer(1)); // doesn't fail.
Run Code Online (Sandbox Code Playgroud)
从链接文档中,我相信Oracle的"堆污染"意味着拥有JVM规范在技术上允许的数据值,但Java编程语言中的泛型规则不允许这样做.
举个例子,假设我们定义一个这样的简单List容器:
class List<E> {
Object[] values;
int len = 0;
List() { values = new Object[10]; }
void add(E obj) { values[len++] = obj; }
E get(int i) { return (E)values[i]; }
}
Run Code Online (Sandbox Code Playgroud)
这是一个通用且安全的代码示例:
List<String> lst = new List<String>();
lst.add("abc");
Run Code Online (Sandbox Code Playgroud)
这是使用原始类型(绕过泛型)但仍然在语义级别遵守类型安全性的代码示例,因为我们添加的值具有兼容类型:
String x = (String)lst.values[0];
Run Code Online (Sandbox Code Playgroud)
扭曲 - 现在这里是与原始类型一起工作的代码,并做了一些坏事,导致"堆污染":
lst.values[lst.len++] = new Integer("3");
Run Code Online (Sandbox Code Playgroud)
上面的代码是有效的,因为数组是类型Object[],可以存储Integer.现在,当我们尝试检索该值时,它将导致ClassCastException- 在检索时(在发生损坏之后的方式),而不是在添加时间:
String y = lst.get(1); // ClassCastException for Integer(3) -> String
Run Code Online (Sandbox Code Playgroud)
请注意,ClassCastException在我们当前的堆栈帧中发生,甚至没有List.get(),因为List.get()由于Java的类型擦除系统,在运行时转换为无操作.
基本上,我们通过绕过泛型来插入Integera List<String>.然后当我们尝试get()一个元素时,列表对象未能坚持它必须返回String(或null)的承诺.
| 归档时间: |
|
| 查看次数: |
1096 次 |
| 最近记录: |