参考资料:
- Java核心技术卷一
秒懂Java类型(Type)系统
https://zhuanlan.zhihu.com/p/64584427
https://blog.csdn.net/ShuSheng0007/article/details/80720406
https://blog.csdn.net/ShuSheng0007/article/details/81809999
一、为什么要使用泛型程序设计
- 意味着编写的代码可以被很多不同类型的对象所重用。
- 使得程序具有更好的可读性和安全性。
二、定义简单的泛型类
package com.lagou.dachang;import java.util.function.Supplier;/*** 简单泛型类* @author yumingxing* @version 1.0* @date 2022/4/18 12:20**/public class Pair<T> {private T first;private T second;public Pair() {this.first = null;this.second = null;}public Pair(T first, T second) {this.first = first;this.second = second;}public T getFirst() {return first;}public void setFirst(T first) {this.first = first;}public T getSecond() {return second;}public void setSecond(T second) {this.second = second;}}
三、泛型方法
/*** 定义泛型方法** @param a 一个* @return {@link T}*/public static <T> T getMiddle(T... a) {return a[a.length / 2];}
//测试泛型方法String middle = ArrayAlg.getMiddle("john", "Q", "Public");//不兼容的类型: Number & Comparable<? extends Number & Comparable<?>> 无法转换为 String//String middle1 = ArrayAlg.getMiddle(3.14,1722,0);
四、泛型变量的限定
有时,类或方法需要对类型变量加以约束。下面是一个典型的例子。我们要计算数组中的最小元素
//public static <T> Pair<T> minmax(T[] a) {public static <T extends Comparable> Pair<T> minmax(T[] a) {if (a == null || a.length == 0) {return null;}T min = a[0];T max = a[0];for (int i = 1; i < a.length; i++) {if (min.compareTo(a[i]) > 0) {min = a[i];}if (max.compareTo(a[i]) < 0) {max = a[i];}}return new Pair<>(min, max);}
T extends Comparable
T extends Comparable & Serializable
五、泛型代码和虚拟机
无论何时定义一个泛型类型,都自动提供了一个相应的原始类型(raw type)。原始类型的名字就是删去类型参数后的泛型类型名。擦除(erased)类型变量,并替换为限定类型(无限定的变量用Object)。
public class Pair {private Object first;private Object second;public Pair() {this.first = null;this.second = null;}public Pair(Object first, Object second) {this.first = first;this.second = second;}public Object getFirst() {return first;}public void setFirst(Object first) {this.first = first;}public Object getSecond() {return second;}public void setSecond(Object second) {this.second = second;}}
当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换。例如,下面这个语句序列
Pair<Employee> buddies = ...;Employee buddy = buddies.getFirst();
类型擦除也会发生在泛型方法中
泛型擦除会对方法带来两个复杂问题,具体可以去了解
1)桥接方法
2)在虚拟机中,用参数类型和返回类型确定一个方法
等知识点
public static <T extends Comparable> T min(T[] a)擦除后public static Comparable min(Comparable[] a)
六、约束与局限性
- 不能用基本类型实例化类型参数 如:Pair
- 运行时类型查询只适用于原始类型 如:if (a instanceof Pair
) - 不能创建参数化类型数组 如:Pair
[] table = new Pair [10]; - 不能实例化类型变量
- 不能构造泛型数组
- 不能在静态 域或方法 中引用类型变量
- 不能抛出或捕获泛型类的实例
七、泛型类型的继承关系
八、通配符类型
Pair<? extend Employee> 表示任何泛型Pair类型,它的类型参数是Employee的子类,如Pair

不能将Pair
解决方法


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

九、无限定通配符
Pair<?>
扩展
Type系统总览
Java Type体系,始于Type接口,其是Java编程语言中所有类型的父接口,是对Java编程语言类型的一个抽象,源码如下所示:


我们需要一个类和一个注解来作为讲解代码的基础。
package com.lagou.dachang.miaodong;import java.io.Serializable;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/*** 泛型类 参数为 T 和 V* @author yumingxing* @version 1.0* @date 2022/4/19 19:40**/public class TypeTest <T,V extends @Custom Number & Serializable> {private Number number;public T t;public V v;public List<T> list = new ArrayList<>();public Map<String,T> map = new HashMap<>();public T[] tArray;public List<T> ltArray;public TypeTest testClass;public TypeTest<T,Integer> testClass2;public Map<? super String,? extends Number> mapWithWildcard;/*** 泛型构造方法,泛型参数为X*/public <X extends Number> TypeTest(X x,T t){number = x;this.t = t;}/*** 泛型构造方法,泛型参数为Y*/public <Y extends T> void method(Y y) {t = y;}}
@Retention(RetentionPolicy.RUNTIME)@Target(value = {ANNOTATION_TYPE, CONSTRUCTOR, FIELD,METHOD, PACKAGE, PARAMETER, TYPE, TYPE_PARAMETER, TYPE_USE})public @interface Custom {}
TypeVariable
类型变量,例如List
此接口源码如下:
interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {//返回此类型参数的上界列表,如果没有上界则放回Object. 例如 V extends @Custom Number & Serializable 这个类型参数,有两个上界,Number 和 SerializableType[] getBounds();//类型参数声明时的载体,例如 `class TypeTest<T, V extends @Custom Number & Serializable>` ,那么V 的载体就是TypeTestD getGenericDeclaration();String getName();//Java 1.8加入 AnnotatedType: 如果这个这个泛型参数类型的上界用注解标记了,我们可以通过它拿到相应的注解AnnotatedType[] getAnnotatedBounds();}
从typeVariable的定义看到其也有一个泛型参数,要求需要是GenericDeclaration 的子类,
//所有可以申明泛型参数的entities都必须实现这个接口public interface GenericDeclaration extends AnnotatedElement {public TypeVariable<?>[] getTypeParameters();}
我们从源码中看到,只有三个类实现了这个接口,分别是:
- java.lang.reflect.Method
- java.lang.reflect.Constructor
- java.lang.Class
所以我们只能在类型(例如Class,Interface)、方法和构造函数这三个地方声明泛型参数
ParameterizedType
参数化类型,即带参数的类型,也可以说带<>的类型。例如List
其源码如下:
interface ParameterizedType extends Type {//获取参数类型<>里面的那些值,例如Map<K,V> 那么就得到 [K,V]的一个数组Type[] getActualTypeArguments();//获取参数类型<>前面的值,例如例如Map<K,V> 那么就得到 MapType getRawType();//获取其父类的类型,例如Map 有一个内部类Entry, 那么在Map.Entry<K,V> 上调用这个方法就可以获得 MapType getOwnerType();}
GenericArrayType
泛型数组类型,用来作为数组的泛型声明类型。例如List
源码如下:
public interface GenericArrayType extends Type {//获取泛型类型数组的声明类型,即获取数组方括号 [] 前面的部分Type getGenericComponentType();}
GenericArrayType 接口只有一个方法getGenericComponentType(),其可以用来获取数组方括号 [] 前面的部分,例如T[],在其上调用getGenericComponentType 就可以获得T. 值得注意的是多维数组得到的是最后一个[] 前面的部分,例如T[][], 得到的是T[].
WildcardType
通配符类型,即带有?的泛型参数, 例如 List<?>中的?,List<? extends Number>里的? extends Number 和List<? super Integer>的? super Integer 。
public interface WildcardType extends Type {// 获取上界Type[] getUpperBounds();//获取下界Type[] getLowerBounds();}
Class
其是Type的一个实现类,是反射的基础,每一个类在虚拟机中都对应一个Calss 对象,我们可以用在运行时从这个Class对象中获取到类型所有信息。

