首先是泛型的新手。现在提问 - 在HashMap.java中,我看到以下内容 -
transient Entry[] table;
which is initiated in constructor as
table = new Entry[capacity];
为什么没有使用类型参数声明?
或者
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
为什么使用类型参数声明for循环中的Entry?
是存在深层概念还是仅仅是负担得起的不一致?
答案 0 :(得分:7)
那是因为创建一个具体参数化类型的数组并不是类型安全的,这就是为什么根本不允许的。
如果您尝试这样的代码,您将收到编译器错误:
List<String>[] arr = new ArrayList<String>[10]; // Compiler error: Generic Array creation
问题是泛型类型是non-reifiable - 它们的类型信息在运行时不可用。同时,数组使用运行时可用的类型信息来执行ArrayStoreCheck
,以查看插入到数组中的元素是否与数组的类型兼容。因此,如果混淆数组和泛型,那么最终可能会在运行时出现令人惊讶的行为。
例如,请考虑以下代码:
List<String>[] arr = new ArrayList<String>[10]; // Suppose this was valid
Object[] objArr = arr; // This is valid assignment. A `List[]` is an `Object[]`
objArr[0] = new ArrayList<Integer>(); // There you go. A disaster waiting at runtime.
String str = arr[0].get(0); // Assigned an `Integer` to a `String`. ClassCastException
所以,如果编译了第一个赋值,那么编译器看起来很好的第4个赋值将在运行时抛出ClassCastException
。
但是,您可以创建一个原始类型数组 - ArrayList
或无限通配符参数化类型 - ArrayList<?>
,因为它们都是完全可再生类型。因此以下数组创建是有效的:
List[] arr = new ArrayList[10];
List<?>[] arr2 = new ArrayList<?>[10];
由于没有与原始类型或无界通配符类型相关联的类型信息,因此在运行时不会丢失任何内容。因此这些类型是可以恢复的,它们是数组的合格组件类型。这就是使用Entry[]
代替Entry<K, V>[]
的原因。
另见: