参考资料:

  1. Java核心技术卷一
  2. 秒懂Java类型(Type)系统

    https://zhuanlan.zhihu.com/p/64584427
    https://blog.csdn.net/ShuSheng0007/article/details/80720406
    https://blog.csdn.net/ShuSheng0007/article/details/81809999

一、为什么要使用泛型程序设计

  • 意味着编写的代码可以被很多不同类型的对象所重用。
  • 使得程序具有更好的可读性和安全性。

二、定义简单的泛型类

  1. package com.lagou.dachang;
  2. import java.util.function.Supplier;
  3. /**
  4. * 简单泛型类
  5. * @author yumingxing
  6. * @version 1.0
  7. * @date 2022/4/18 12:20
  8. **/
  9. public class Pair<T> {
  10. private T first;
  11. private T second;
  12. public Pair() {
  13. this.first = null;
  14. this.second = null;
  15. }
  16. public Pair(T first, T second) {
  17. this.first = first;
  18. this.second = second;
  19. }
  20. public T getFirst() {
  21. return first;
  22. }
  23. public void setFirst(T first) {
  24. this.first = first;
  25. }
  26. public T getSecond() {
  27. return second;
  28. }
  29. public void setSecond(T second) {
  30. this.second = second;
  31. }
  32. }

三、泛型方法

  1. /**
  2. * 定义泛型方法
  3. *
  4. * @param a 一个
  5. * @return {@link T}
  6. */
  7. public static <T> T getMiddle(T... a) {
  8. return a[a.length / 2];
  9. }
  1. //测试泛型方法
  2. String middle = ArrayAlg.getMiddle("john", "Q", "Public");
  3. //不兼容的类型: Number & Comparable<? extends Number & Comparable<?>> 无法转换为 String
  4. //String middle1 = ArrayAlg.getMiddle(3.14,1722,0);

四、泛型变量的限定

有时,类或方法需要对类型变量加以约束。下面是一个典型的例子。我们要计算数组中的最小元素

  1. //public static <T> Pair<T> minmax(T[] a) {
  2. public static <T extends Comparable> Pair<T> minmax(T[] a) {
  3. if (a == null || a.length == 0) {
  4. return null;
  5. }
  6. T min = a[0];
  7. T max = a[0];
  8. for (int i = 1; i < a.length; i++) {
  9. if (min.compareTo(a[i]) > 0) {
  10. min = a[i];
  11. }
  12. if (max.compareTo(a[i]) < 0) {
  13. max = a[i];
  14. }
  15. }
  16. return new Pair<>(min, max);
  17. }

T extends Comparable
T extends Comparable & Serializable

五、泛型代码和虚拟机

无论何时定义一个泛型类型,都自动提供了一个相应的原始类型(raw type)。原始类型的名字就是删去类型参数后的泛型类型名。擦除(erased)类型变量,并替换为限定类型(无限定的变量用Object)。

  1. public class Pair {
  2. private Object first;
  3. private Object second;
  4. public Pair() {
  5. this.first = null;
  6. this.second = null;
  7. }
  8. public Pair(Object first, Object second) {
  9. this.first = first;
  10. this.second = second;
  11. }
  12. public Object getFirst() {
  13. return first;
  14. }
  15. public void setFirst(Object first) {
  16. this.first = first;
  17. }
  18. public Object getSecond() {
  19. return second;
  20. }
  21. public void setSecond(Object second) {
  22. this.second = second;
  23. }
  24. }

当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换。例如,下面这个语句序列

  1. Pair<Employee> buddies = ...;
  2. Employee buddy = buddies.getFirst();

类型擦除也会发生在泛型方法中
泛型擦除会对方法带来两个复杂问题,具体可以去了解
1)桥接方法
2)在虚拟机中,用参数类型和返回类型确定一个方法
等知识点

  1. public static <T extends Comparable> T min(T[] a)
  2. 擦除后
  3. public static Comparable min(Comparable[] a)

六、约束与局限性

  1. 不能用基本类型实例化类型参数 如:Pair
  2. 运行时类型查询只适用于原始类型 如:if (a instanceof Pair)
  3. 不能创建参数化类型数组 如:Pair[] table = new Pair[10];
  4. 不能实例化类型变量
  5. 不能构造泛型数组
  6. 不能在静态 域或方法 中引用类型变量
  7. 不能抛出或捕获泛型类的实例

七、泛型类型的继承关系

image.png
image.png

八、通配符类型

Pair<? extend Employee> 表示任何泛型Pair类型,它的类型参数是Employee的子类,如Pair,但不是Pair

image.png

不能将Pair传递给这个方法

解决方法

image.png

image.png

?super Manager 这个通配符限制为Manager的所有超类型。

image.png

九、无限定通配符

Pair<?>

扩展

Type系统总览

Java Type体系,始于Type接口,其是Java编程语言中所有类型的父接口,是对Java编程语言类型的一个抽象,源码如下所示:

image.png
image.png

我们需要一个类和一个注解来作为讲解代码的基础。

  1. package com.lagou.dachang.miaodong;
  2. import java.io.Serializable;
  3. import java.util.ArrayList;
  4. import java.util.HashMap;
  5. import java.util.List;
  6. import java.util.Map;
  7. /**
  8. * 泛型类 参数为 T 和 V
  9. * @author yumingxing
  10. * @version 1.0
  11. * @date 2022/4/19 19:40
  12. **/
  13. public class TypeTest <T,V extends @Custom Number & Serializable> {
  14. private Number number;
  15. public T t;
  16. public V v;
  17. public List<T> list = new ArrayList<>();
  18. public Map<String,T> map = new HashMap<>();
  19. public T[] tArray;
  20. public List<T> ltArray;
  21. public TypeTest testClass;
  22. public TypeTest<T,Integer> testClass2;
  23. public Map<? super String,? extends Number> mapWithWildcard;
  24. /**
  25. * 泛型构造方法,泛型参数为X
  26. */
  27. public <X extends Number> TypeTest(X x,T t){
  28. number = x;
  29. this.t = t;
  30. }
  31. /**
  32. * 泛型构造方法,泛型参数为Y
  33. */
  34. public <Y extends T> void method(Y y) {
  35. t = y;
  36. }
  37. }
  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(value = {ANNOTATION_TYPE, CONSTRUCTOR, FIELD,
  3. METHOD, PACKAGE, PARAMETER, TYPE, TYPE_PARAMETER, TYPE_USE})
  4. public @interface Custom {
  5. }

TypeVariable

类型变量,例如List中的T, Map中的K和V,我们的测试类class TypeTest中的T和V。

此接口源码如下:

  1. interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
  2. //返回此类型参数的上界列表,如果没有上界则放回Object. 例如 V extends @Custom Number & Serializable 这个类型参数,有两个上界,Number 和 Serializable
  3. Type[] getBounds();
  4. //类型参数声明时的载体,例如 `class TypeTest<T, V extends @Custom Number & Serializable>` ,那么V 的载体就是TypeTest
  5. D getGenericDeclaration();
  6. String getName();
  7. //Java 1.8加入 AnnotatedType: 如果这个这个泛型参数类型的上界用注解标记了,我们可以通过它拿到相应的注解
  8. AnnotatedType[] getAnnotatedBounds();
  9. }

从typeVariable的定义看到其也有一个泛型参数,要求需要是GenericDeclaration 的子类,

  1. //所有可以申明泛型参数的entities都必须实现这个接口
  2. public interface GenericDeclaration extends AnnotatedElement {
  3. public TypeVariable<?>[] getTypeParameters();
  4. }

我们从源码中看到,只有三个类实现了这个接口,分别是:

  • java.lang.reflect.Method
  • java.lang.reflect.Constructor
  • java.lang.Class

所以我们只能在类型(例如Class,Interface)、方法构造函数这三个地方声明泛型参数

ParameterizedType

参数化类型,即带参数的类型,也可以说带<>的类型。例如List, User 等。

其源码如下:

  1. interface ParameterizedType extends Type {
  2. //获取参数类型<>里面的那些值,例如Map<K,V> 那么就得到 [K,V]的一个数组
  3. Type[] getActualTypeArguments();
  4. //获取参数类型<>前面的值,例如例如Map<K,V> 那么就得到 Map
  5. Type getRawType();
  6. //获取其父类的类型,例如Map 有一个内部类Entry, 那么在Map.Entry<K,V> 上调用这个方法就可以获得 Map
  7. Type getOwnerType();
  8. }

GenericArrayType

泛型数组类型,用来作为数组的泛型声明类型。例如List[] ltArray, T[] tArray两个数组,其中List[], 和T[]就是GenericArrayType`类型。

源码如下:

  1. public interface GenericArrayType extends Type {
  2. //获取泛型类型数组的声明类型,即获取数组方括号 [] 前面的部分
  3. Type getGenericComponentType();
  4. }

GenericArrayType 接口只有一个方法getGenericComponentType(),其可以用来获取数组方括号 [] 前面的部分,例如T[],在其上调用getGenericComponentType 就可以获得T. 值得注意的是多维数组得到的是最后一个[] 前面的部分,例如T[][], 得到的是T[].

WildcardType

通配符类型,即带有?的泛型参数, 例如 List<?>中的?,List<? extends Number>里的? extends Number 和List<? super Integer>的? super Integer 。

  1. public interface WildcardType extends Type {
  2. // 获取上界
  3. Type[] getUpperBounds();
  4. //获取下界
  5. Type[] getLowerBounds();
  6. }

Class

其是Type的一个实现类,是反射的基础,每一个类在虚拟机中都对应一个Calss 对象,我们可以用在运行时从这个Class对象中获取到类型所有信息。