泛型
泛型是JDK1.5之后的一项新增特性, 它的本质是参数化类型(Parametersized Type)的应用,
即所操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法的创建中,分别为泛型类、泛型接口和泛型方法。
Java中的泛型只在程序源代码中存在, 在编译后的字节码文件中就已经替换为原来的原生类型(Raw Type), 并在相应的地方插入强制类型装换代码。
对于运行期的Java语言来说,ArrayList<int> 与 ArrayList<String> 就是同一个类,泛型技术实际上就是Java语言的一颗语法糖,Java语言中的泛型实现方法称为类型擦除,基于这种方法的实现的泛型称为伪泛型。
java 字节码层面
源代码
public class MyListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("test");
String list1 = list.get(0);
System.out.println(list1);
System.out.println(list);
}
}
编译命令
javac -encoding UTF-8 MyListTest.java
编译为class文件
public class MyListTest {
public MyListTest() {
}
public static void main(String[] var0) {
ArrayList var1 = new ArrayList();
var1.add("test");
String var2 = (String)var1.get(0); // 这里插入强制类型转换
System.out.println(var2);
System.out.println(var1);
}
}
javap查看字节码
javap -c MyListTest
警告: 二进制文件MyListTest包含com.james.example.MyListTest
Compiled from "MyListTest.java"
public class com.james.example.MyListTest {
public com.james.example.MyListTest();
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: aload_1
9: ldc #4 // String test
11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: aload_1
18: iconst_0
19: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 这里获取的是Object类型
24: checkcast #7 // class java/lang/String 类型转换指令
27: astore_2
28: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
31: aload_2
32: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
35: getstatic #8 /
Java中获取泛型的类型信息
Java泛型的引入,各种场景(虚拟机解析, 反射等)下的方法调用都可能对原有的基础产生影响和新需求, 如在泛型类中如何获取传入的参数化类型等。基于此,JCP组织对虚拟机规范做出了相应的修改,引入了诸如Signature, LocalVariableType Table 等新属性解决伴随泛型而来的参数化类型的识别问题。
Signature 是其中最重要的一项属性,作用是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型不是原生类型,而是包括了参数化类型的信息。
<>: 念做typeof
List<E>: E称为类型参数变量
List<Integer>: Integer称为实际类型参数
List<Integer>: 整个List<Integer>称为参数化类型(对应着java.lang.reflect.ParameterizedType接口)
实例化子类中获取泛型
定义一个MyType
public class MyType<T> {
public Class<T> getTClass() {
return (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
}
测试
MyType<String> myType = new MyType<String>(){}; //注意这里是实例化的
// 在类的外部获取
Type actualTypeArguments = ((ParameterizedType) myType.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
Type rawType = ((ParameterizedType) myType.getClass().getGenericSuperclass()).getRawType();
System.out.println(actualTypeArguments);
System.out.println(rawType);
System.out.println(myType.getTClass());
测试结果
class java.lang.String
class com.james.example.type.MyType
class java.lang.String
未实例化子类无法获取泛型
测试
MyType<String> myType = new MyType<>(); //注意这里是未实例化的
Type actualTypeArguments = ((ParameterizedType) myType.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
Type rawType = ((ParameterizedType) myType.getClass().getGenericSuperclass()).getRawType();
System.out.println(actualTypeArguments);
System.out.println(rawType);
System.out.println(myType.getTClass());
结果
Exception in thread "main" java.lang.ClassCastException: class java.lang.Class cannot be cast to class java.lang.reflect.ParameterizedType (java.lang.Class and java.lang.reflect.ParameterizedType are in module java.base of loader 'bootstrap')
at com.james.example.type.MyListTest.main(MyListTest.java:23)
List测试
ArrayList<String> list3 = new ArrayList<String>(){};
System.out.println(((ParameterizedType)list3.getClass().getGenericSuperclass()).getRawType());
System.out.println(((ParameterizedType)list3.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
ArrayList<String> list6 = new ArrayList<>();// 这里new ArrayList<String>() 结果也是同new ArrayList<>()
System.out.println(((ParameterizedType)list6.getClass().getGenericSuperclass()).getRawType());
System.out.println(((ParameterizedType)list6.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
class java.util.ArrayList
class java.lang.String
class java.util.AbstractList
E
总结
getGenericInterfaces()
getGenericSuperclass()
import java.lang.reflect.ParameterizedType; getActualTypeArguments()
import java.lang.reflect.Type;
Signature属性的出现,Java泛型擦除法所谓的擦除,只是对方法的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们能通过反射手段获取参数化类型的根本依据。