读《深入理解java虚拟机》的时候读到泛型和类型擦除。经过javac编译后泛型类型都会变为原生类型,文中还指出,泛型技术其实是一种伪泛型,List<String> 和 List<Integer>在字节码中都是List,Java中称为类型擦除,但是在类、方法表、字段表中加入了Signature属性用以记录了泛型的信息。
读完后大感惊讶,我原来以为,List<String> 和 List<Integer>是两个完全不同的对象,然而在编译阶段就被统一打成了List.....只是加了个签名记录。由此,翻了翻源码找寻答案。
泛型和类型擦除
java代码,创建List<String>和List<Integer>
public class TypeTest {
public static void main(String[] args) {
List<String> strings = new ArrayList<String>();
List<Integer> integers = new ArrayList();
}
}
class文件反编译后的代码,泛型的确被擦除了。
public class TypeTest {
public TypeTest() {
}
public static void main(String[] args) {
new ArrayList();
new ArrayList();
}
}
查看code中的字节码指令:javap -c TypeTest 字节码中的说明新建的对象的确是java/util/ArrayList这个类型,并未发现泛型的踪迹。
Compiled from "TypeTest.java"
public class sourcecode.TypeTest {
public sourcecode.TypeTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: new #2 // class java/util/ArrayList
11: dup
12: invokespecial #3 // Method java/util/ArrayList."<init>":()V
15: astore_2
16: return
}
打印Class文件字节码内容 javap -verbose TypeTest
Classfile /D:/algorithm/target/classes/sourcecode/TypeTest.class
Last modified 2017-9-14; size 635 bytes
MD5 checksum 00c2ab23b321a7bd4a5bdaaf5a125d8c
Compiled from "TypeTest.java"
public class sourcecode.TypeTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#25 // java/lang/Object."<init>":()V
#2 = Class #26 // java/util/ArrayList
#3 = Methodref #2.#25 // java/util/ArrayList."<init>":()V
#4 = Class #27 // sourcecode/TypeTest
#5 = Class #28 // java/lang/Object
#6 = Utf8 <init>
#7 = Utf8 ()V
#8 = Utf8 Code
#9 = Utf8 LineNumberTable
#10 = Utf8 LocalVariableTable
#11 = Utf8 this
#12 = Utf8 Lsourcecode/TypeTest;
#13 = Utf8 main
#14 = Utf8 ([Ljava/lang/String;)V
#15 = Utf8 args
#16 = Utf8 [Ljava/lang/String;
#17 = Utf8 strings
#18 = Utf8 Ljava/util/List;
#19 = Utf8 integers
#20 = Utf8 LocalVariableTypeTable
#21 = Utf8 Ljava/util/List<Ljava/lang/String;>;
#22 = Utf8 Ljava/util/List<Ljava/lang/Integer;>;
#23 = Utf8 SourceFile
#24 = Utf8 TypeTest.java
#25 = NameAndType #6:#7 // "<init>":()V
#26 = Utf8 java/util/ArrayList
#27 = Utf8 sourcecode/TypeTest
#28 = Utf8 java/lang/Object
{
public sourcecode.TypeTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lsourcecode/TypeTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: new #2 // class java/util/ArrayList
11: dup
12: invokespecial #3 // Method java/util/ArrayList."<init>":()V
15: astore_2
16: return
LineNumberTable:
line 13: 0
line 15: 8
line 18: 16
LocalVariableTable:
Start Length Slot Name Signature
0 17 0 args [Ljava/lang/String;
8 9 1 strings Ljava/util/List;
16 1 2 integers Ljava/util/List;
LocalVariableTypeTable:
Start Length Slot Name Signature
8 9 1 strings Ljava/util/List<Ljava/lang/String;>;
16 1 2 integers Ljava/util/List<Ljava/lang/Integer;>;
}
SourceFile: "TypeTest.java"
在Code属性下,LocalVariableTypeTable属性中存在Signature属性,其中明显的指出strings和integers这两个对象的泛型。
java1.5因出现泛型而随之出现的就是Type接口及其子接口GenericArrayType(泛型数组), ParameterizedType(参数化类型), TypeVariable(类型变量), WildcardType(通配符类型), 用以表示扩展的类型。
测试如下:
public class TypeTest {
List<Integer> integers;
@Test
public void test() throws NoSuchFieldException {
//获取integers字段
Field field = TypeTest.class.getDeclaredField("integers");
//获取integers字段的类型Type
System.out.println("Type:" + field.getType());//Type:interface java.util.List
System.out.println(field.getType() instanceof Type);//true
//获取integers字段的泛型
System.out.println("GenericType:" + field.getGenericType());//GenericType:java.util.List<java.lang.Integer>
//integers字段的泛型是否是一种Type
System.out.println(field.getGenericType() instanceof Type);//true
//integers字段的泛型是否是ParameterizedType的实例
System.out.println(field.getGenericType() instanceof ParameterizedType);//true
//integers字段的泛型是否是GenericArrayType的实例
System.out.println(field.getGenericType() instanceof GenericArrayType);//false
//integers字段的泛型是否是TypeVariable的实例
System.out.println(field.getGenericType() instanceof TypeVariable);//false
//integers字段的泛型是否是WildcardType的实例
System.out.println(field.getGenericType() instanceof WildcardType);//false
}
}
integers首先是个List,而List中存放的是Integer类型的数据,泛型的作用是明确内层参数的类型,也可以说所需参数的类型。从test()测试方法中可以看出,integers字段的中Type属于通用级别,且只表示最外层类型,或者说主类型。
integers的泛型属于ParameterizedType -参数化类型。
另附上Field部分代码
public final class Field extends AccessibleObject implements Member {
private Class<?> type;
// Generics and annotations support --明确注释了 为了支持泛型和注解
private transient String signature;
// generic info repository; lazily initialized --泛型信息仓库,延迟初始化,不调用时为null
private transient FieldRepository genericInfo;
private String getGenericSignature() {return signature;}
/**
* Returns a {@code Class} object that identifies the
* declared type for the field represented by this
* {@code Field} object.
*返回字段声明类型的对象
*/
public Class<?> getType() {
return type;
}
/**
* Returns a {@code Type} object that represents the declared type for
* the field represented by this {@code Field} object.
*
* <p>If the {@code Type} is a parameterized type, the
* {@code Type} object returned must accurately reflect the
* actual type parameters used in the source code.
* 如果该类型时参数化的类型,返回的类型对象必须反映源代码中使用的实际类型参数
* <p>If the type of the underlying field is a type variable or a
* parameterized type, it is created. Otherwise, it is resolved.
* 如果基础字段的类型是类型变量或参数化类型,则会创建它。否则解析它。
* @since 1.5省略异常说明
*/
public Type getGenericType() {
//泛型签名如果不为空,返回getGenericInfo().getGenericType()方法解析singture字段。
//怎么解析和转换的就不多说了
if (getGenericSignature() != null)
return getGenericInfo().getGenericType();
else
//泛型为空,则返回Type类型
return getType();
}
调试可知,其signature的确是字节码中Signature属性- Ljava/util/List<Ljava/lang/Integer;>;
= =说好的reflect包源码呢...下次说type