JVM基础 -- 伪泛型
本文将从
JVM字节码
的角度解释一下Java为什么是伪泛型
伪泛型
- Java是
伪泛型
的,泛型在Java中只是语法糖
,是通过类型擦除
和强制转换
来实现的 - 在
运行期
,ArrayList<Integer>
与ArrayList<String>
就是同一个类
,在Java中并不存在类似与ArrayList<Integer>
这种泛型类型
代码
1 | public class FakeGeneric { |
字节码
1 | public static void main(java.lang.String[]); |
new HashMap<>()
在Java代码中,创建map时是带有泛型信息的
1 | Map<Father, Father> map = new HashMap<>(); |
对应的JVM字节码
1 | 0: new #2 // class java/util/HashMap |
在map的创建过程中并没有任何泛型信息
,已经可以初步判断,Java并不存在HashMap<Father,Father>
这样的泛型类型
,是**伪泛型
**
关于那4个指令的具体含义,可参照博文「字节码 - 方法重载 + 方法重写」,这里不再赘述
map.put(father, son)
在Java代码中,Map接口的put
方法签名也是带有泛型信息的
1 | V put(K key, V value); |
对应的JVM字节码
1 | 27: invokeinterface #8,3 // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; |
在put
操作中也是没有任何泛型信息
,只是简单地处理为java.lang.Object
,这也是我们常说的**类型擦除
**
map.get(father)
在Java代码中,Map接口的get
方法签名也是带有泛型信息的
1 | V get(Object key); |
对应的JVM字节码
1 | 35: invokeinterface #9,2 // InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object; |
在get
操作中也是没有任何泛型信息
,只是简单地处理为java.lang.Object
,与put
操作类似
checkFather(Father father)
Java代码
1 | checkFather(map.get(father)); |
对应的字节码(排除get
操作的字节码,前面已经提及过)
1 | 40: checkcast #4 // class me/zhongmingmao/test/FakeGeneric$Father |
- 非常值得注意的是在调用
checkFather
方法之前有一个**checkcast
指令,这就是将之前的get
操作得到的对象进行强制转换
**,由Object
转换成Father
- 那么JVM在执行
checkcast
的时候怎么知道要转换成什么类型
呢?前面分析字节码操作都是没有附带任何泛型信息的 - 答案就是在字节码文件中的**
LocalVariableTypeTable
**属性,因为Java是伪泛型
的,因此需要这么一个属性详细记录的泛型信息,才能进行强制转换
伴随的问题
Java的伪泛型会带来一些语法上的尴尬
,例如重载
1 | public static void method(List<String> list){ |
List<String> list
与List<Integer> list
在Java语法层面
是不同的参数类型
,应该属于重载
List<String> list
与List<Integer> list
在JVM层面
,会进行所谓的类型擦除
(其实只是伪泛型的一层伪装
),是同一参数类型
,方法具有相同的特征签名
(signature
)
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.