HashMap 实现中的泛型

Generics in HashMap implementation

在Java实现中,我发现

 transient Entry[] table; 
 which is initiated in constructor as
 table = new Entry[capacity];

我知道并理解不允许创建泛型数组,但我不明白的是整个事情是如何运作的。我的意思是当我们做类似

的事情时
HashMap<Integer, String> hMap = new HashMap<Integer, String>();

以上代码如何导致创建 <Integer, String>

类型的条目数组

好吧,很少有人不明白我在问什么。换句话说,我要问的是做

这样的事情有什么意义
HashMap<Integer, String> hMap = new HashMap<Integer, String>();

当它没有导致

Entry<Integer, String>

泛型是编译时安全的。在 运行 时间,地图只知道对象。这被称为类型擦除。更吓唬你,下面的代码运行没问题:

Map<Integer, Integer> safeMap = new HashMap<>();
Map unsafeMap = safeMap;
unsafeMap.put("hello", "world");

你会在编译时收到警告,因为你使用的是原始地图而不是通用地图,但在 运行 时,根本不会进行任何检查,因为地图是能够存储任何对象的好旧地图。只有编译器会阻止您在映射或整数中添加字符串。

该实现创建了一个 Entry<K,V> 个类型为

的对象数组
static class Entry<K,V> implements Map.Entry<K,V>

不提供通用类型参数 (source)。这是允许的,但它伴随着编译器不再保证类型安全的理解。例如,在代码的其他地方你可以写

Entry<K,V> e = table[bucketIndex];

编译器会让你这样做。如果您确定总是将 table[] 的元素设置为 nullEntry<K,V>,那么您就知道赋值是正确的。

这没有问题的原因是 Java 中的泛型类型是通过类型擦除实现的,即 Entry<K,V> 对象 Entry<Integer,Integer>Entry<String,Long>.

尝试以这种方式思考 Java 泛型:类型参数仅适用于 引用类型表达式的静态类型 而不适用于 实际实例的类型在运行时被引用值引用。

我发现上述关键是在阅读 Java 代码时培养正确的直觉。所以下次你看到

new HashMap<Integer, String>()

阅读如下:"This is an instance creation expression of the type HashMap<Integer, String>. At runtime this expression will yield a reference to an instance of the HashMap class."只要编译器能够精确地跟踪你对该表达式的结果做了什么,它就可以保持这确实是一个HashMap<Integer, String>的知识,但是仅此而已。

现在,由于静态类型系统不够强大,无法跟踪数组组件类型的类型参数(Java 的数组类型是协变的这一事实在这里发挥了重要作用),代码是被迫打破静态类型的安全网络。关键的观察是,就其本身而言,这不会使代码 不正确 ,它只会限制编译器查找编程错误的能力。这就是为什么 Java 允许您进行未经检查的从原始类型到泛型类型的转换,尽管并非没有警告标记您已经离开静态类型安全省份的地方。