泛型

作者: couriravant | 来源:发表于2020-01-10 16:04 被阅读0次
  • 泛型的好处:

编译期强类型检查、无需进行显式类型转换。

  • 通常情况下,T,E,K,V,? 是这样约定的:

? 表示不确定的 java 类型
T (type) 表示具体的一个java类型
K V (key value) 分别代表java键值中的Key Value
E (element) 代表Element

? 和 T 的区别

  • T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。
    通配符可以使用超类限定而类型参数不行
  • 类型参数 T 只具有 一种 类型限定方式:
T extends A

但是通配符 ? 可以进行 两种限定:

? extends A
? super A

其他注意点:
不能new T
不能传<String> 给 <Object>,因为泛型不是协变的。

  • 泛型擦除的原因:

Java 引入泛型擦除的原因是避免因为引入泛型而导致运行时创建不必要的类。那我们其实就可以通过定义类的方式,在类信息中保留泛型信息,从而在运行时获得这些泛型信息。所以,想 ArrayList<Integer> 和 ArrayList<String> 这两个实例,其类实例是同一个。

简而言之,Java 的泛型擦除是有范围的,即类定义中的泛型是不会被擦除的。

  • 运行时通过反射获取泛型类型

signature 属性

Java泛型的擦除并不是对所有使用泛型的地方都会擦除的,部分地方会保留泛型信息。
Class文件实际保留了泛型信息,位于在Signature属性中,类型擦除只是对Code属性中的字节码
可以通过反射读取相关信息,其核心是Type类型,根据其不同的子类型,获取泛型信息,具体方法可以查看API文档

image
public class TypeTest<T>{
      public List<T> list;
}

我通过反射可以获取到这list的声明类型 java.util.List<T>

Field list = TypeTest.class.getField("list");
Type genericType1 = list.getGenericType();
System.out.println("参数类型1:" + genericType1.getTypeName());

输出

参数类型1:java.util.List<T>
/大括号非常重要,相当于匿名内部类
Map<String, Integer> map = new HashMap<String, Integer>() {};
Type type = map.getClass().getGenericSuperclass();  
ParameterizedType parameterizedType = ParameterizedType.class.cast(type);  
for (Type typeArgument : parameterizedType.getActualTypeArguments()) {  
    System.out.println(typeArgument.getTypeName());  
} 
/* Output 
java.lang.String 
java.lang.Integer 
*/  

上面这段代码展示了如何获取 map 这个实例所对应类的泛型信息,其中最重要的就是第一行 map 实例的创建,是一个匿名内部类且为 HashMap 的子类,泛型参数限定为 String 和 Integer。

Java 引入泛型擦除的原因是避免因为引入泛型而导致运行时创建不必要的类,所以我们可以通过定义类的方式在类信息中保留泛型信息,从而在运行时获得这些泛型信息,所以 Java 的泛型擦除是有范围的,即类定义中的泛型是不会被擦除的。

List<Integer> list = new ArrayList<>();
list.getClass().getGenericSuperclass(); //获取不到泛型信息
List<Integer> list1 = new ArrayList() {};
list1.getClass().getGenericSuperclass(); //可以获取到泛型信息

可以看到第一个list由于是在运行时创建的对象所以由于泛型擦除是无法获取泛型信息的,因为运行时对象本质是方法的调用(真正调用了以后才会创建),在运行时创建的对象是没有办法通过反射获取其中的类型的。
第二个是可以获取的,因为后边加了{},这就使得这个list成为了一个匿名内部类且父类是List,子类是可以调用父类的构造方法的,加了之后这个list1就不是运行时创建的对象了而是编译时创建的,所以是可以获取泛型类型的。

我理解跟下面这个通过子类获取父类泛型类型是一样的:

class Child extends Parent<String>

//Child类中可通过如下代码获取到带<String>的Parent
((ParameterizedType)getClass().getGenericSuperclass().getActualTypeArguments()[0];

Java在编译的时候会检测父类的范型信息,因为子类声明了范型的类型并且在子类的代码中会使用到该类型,所以java会在生成的class中记录该子类声明的范型类型,所以只有在这种情况下运行时通过反射API可以取到该范型的类型。

相关文章

  • 泛型 & 注解 & Log4J日志组件

    掌握的知识 : 基本用法、泛型擦除、泛型类/泛型方法/泛型接口、泛型关键字、反射泛型(案例) 泛型 概述 : 泛型...

  • 【泛型】通配符与嵌套

    上一篇 【泛型】泛型的作用与定义 1 泛型分类 泛型可以分成泛型类、泛型方法和泛型接口 1.1 泛型类 一个泛型类...

  • 泛型的使用

    泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法 泛型类 泛型接口 泛型通配符 泛型方法 静态方法与...

  • Java 泛型

    泛型类 例如 泛型接口 例如 泛型通配符 泛型方法 类中的泛型方法 泛型方法与可变参数 静态方法与泛型 泛型上下边...

  • 探秘 Java 中的泛型(Generic)

    本文包括:JDK5之前集合对象使用问题泛型的出现泛型应用泛型典型应用自定义泛型——泛型方法自定义泛型——泛型类泛型...

  • Web笔记-基础加强

    泛型高级应用 自定义泛型方法 自定义泛型类 泛型通配符? 泛型的上下限 泛型的定义者和泛型的使用者 泛型的定义者:...

  • 重走安卓进阶路——泛型

    ps.原来的标题 为什么我们需要泛型? 泛型类、泛型接口和泛型方法(泛型类和泛型接口的定义与泛型方法辨析); 如何...

  • Kotlin泛型的高级特性(六)

    泛型的高级特性1、泛型实化2、泛型协变3、泛型逆变 泛型实化 在Java中(JDK1.5之后),泛型功能是通过泛型...

  • Java 19-5.1泛型

    泛型类定义泛型类可以规定传入对象 泛型类 和泛型方法 泛型接口 如果实现类也无法确定泛型 可以在继承类中确定泛型:

  • 【Swift】泛型常见使用

    1、Swift泛型4种 泛型函数泛型类型泛型协议泛型约束 2、泛型约束3种 继承约束:泛型类型 必须 是某个类的子...

网友评论

      本文标题:泛型

      本文链接:https://www.haomeiwen.com/subject/wzopactx.html