一、泛型
泛型的定义
泛型的本质是为了参数化类型,也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
泛型的作用
- 代码复用,使用场景:多种数据类型执行相同代码
- 使用泛型时指定类型,就不需要强制类型转换,避免类型转换异常“java.lang.ClassCastException”
PESC规则
PECS原则全称"Producer Extends, Consumer Super",即上界生产,下界消费。 指的是泛型通配符的两种使用方式:
- ?extends X 表示类型的上界,类型参数是 X 本身和他的子类
- ?super X 表示类型的下界,类型参数是 X 本身和他的超类
Java虚拟机是如何实现泛型的
Java泛型是伪泛型,只在源码中存在,在编译后的字节码文件中会进行类型擦除,变为裸类型。所以编码时以下代码认为是同一函数
既然擦除了,那在泛型类中如何获取传入的参数化类型的呢?
- 引入Signature属性存储一个方法在字节码层面的特征签名,其中包括了参数化类型的信息。
java虚拟机引入了诸如 Signature、LocalVariableTypeTable 等属性用于解决伴随泛型而来的参数类型的识别问题,Signature 是其中最重要的一项属性,它的作用就是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型并不是原生类型,而是包括了参数化类型的信息。
所以擦除法所谓的擦除,仅仅是对方法的 Code 属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们能通过反射手段取得参数化类型的根本依据。
如何用反射获取泛型真实类型?
Type type = getClass().getMethod("getList").getGenericReturnType();
if (type instanceof ParameterizedType) {
Type[] actualTypes = ((ParameterizedType)type).getActualTypeArguments();
System.out.println(actualTypes[0]); // 打印真实类型
}
应用场景:Gson反序列化时解析new TypeToken<List<String>>(){}