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[]
的元素设置为 null
或 Entry<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 允许您进行未经检查的从原始类型到泛型类型的转换,尽管并非没有警告标记您已经离开静态类型安全省份的地方。
在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[]
的元素设置为 null
或 Entry<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 允许您进行未经检查的从原始类型到泛型类型的转换,尽管并非没有警告标记您已经离开静态类型安全省份的地方。