目录
[示例一:使用 T 定义泛型类](#示例一:使用 T 定义泛型类)
[示例二:使用 E 表示集合元素](#示例二:使用 E 表示集合元素)
[示例三:使用 K 和 V 表示键值对](#示例三:使用 K 和 V 表示键值对)
[示例四:使用 ? 通配符处理未知类型](#示例四:使用 ? 通配符处理未知类型)
[四、通配符 ? 的扩展用法](#四、通配符 ? 的扩展用法)
[上限通配符:? extends T](#上限通配符:? extends T)
[下限通配符:? super T](#下限通配符:? super T)
[六、泛型的类型表示:Type 接口的实现](#六、泛型的类型表示:Type 接口的实现)
[Class :非泛型类型](#Class :非泛型类型)
Java 泛型(Generics)是 Java 5 引入的重要特性之一,它允许在类、接口和方法中使用类型参数,从而实现类型安全的复用代码。泛型不仅提高了代码的可重用性,还增强了编译时的类型检查,避免了运行时的类型转换错误。
元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。如:Collection<E>,List<E>,ArrayList<E>这个<E>就是类型参数,即泛型
在使用泛型的过程中,我们经常会看到 T、E、K、V、? 等符号,它们分别代表不同的类型参数。本文将详细解析这些符号的含义、用途以及最佳实践,帮助你不再"傻傻分不清"。

一、概述
泛型的核心思想是 "参数化类型",即把类型作为参数传递给类或方法。例如:
java
List<String> list = new ArrayList<>();
这里的 String 就是类型参数,表示这个列表只能存放字符串类型的元素。
二、为什么使用泛型
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
-
保证了类型的安全性:在没有泛型之前,从集合中读取到的每一个对象都必须进行类型转换,如果不小心插入了错误的类型对象,在运行时的转换处理就会出错
-
消除强制转换:泛型的一个附带好处是,消除源代码中的许多强制类型转换,这使得代码更加可读,并且减少了出错机会
-
避免了不必要的装箱、拆箱操作,提高程序的性能
-
提高了代码的重用性
三、常见泛型类型参数的含义与用途
Java 泛型中并没有强制规定必须使用哪些字母作为类型参数,但为了代码的可读性和一致性,社区中形成了一些约定俗成的命名习惯。
类型参数 | 含义 | 常见使用场景 |
---|---|---|
T | Type(类型) | 表示任意具体类型,常用于类、接口、方法级别的泛型定义 |
E | Element(元素) | 多用于集合类中,表示集合中的元素类型,如 List<E> |
K | Key(键) | 通常用于 Map 中的键类型,如 Map<K, V> |
V | Value(值) | 通常用于 Map 中的值类型,如 Map<K, V> |
N | Number(数字) | 表示数值类型,如 Number 的子类 |
? | Unknown(未知) | 表示不确定的类型,用于通配符(Wildcard) |
S, U, V | 第二、第三、第四个类型参数 | 当一个泛型需要多个类型参数时使用 |
示例一:使用 T 定义泛型类
java
public class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
调用方式:
java
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
Box<Integer> integerBox = new Box<>();
integerBox.setItem(123);
示例二:使用 E 表示集合元素
java
public interface List<E> extends Collection<E> {
// ...
}
示例三:使用 K 和 V 表示键值对
java
public interface Map<K, V> {
V put(K key, V value);
V get(Object key);
}
示例四:使用 ? 通配符处理未知类型
java
public static void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
此方法可以接受任何类型的 List,比如 List、List等。
四、通配符 ? 的扩展用法
通配符 ? 可以配合上下限使用,实现更灵活的类型约束。
无界通配符:?
适用于不知道或不关心具体类型的情况。
java
List<?> anyList = new ArrayList<String>();
上限通配符:? extends T
表示某种类型及其子类,适合"只读"操作。
java
public static void addNumbers(List<? extends Number> list) {
for (Number number : list) {
System.out.println(number.doubleValue());
}
}
"
注意:不能向这种列表中添加元素(除了 null),因为编译器无法确定具体的类型。
下限通配符:? super T
表示某种类型及其父类,适合"写入"操作。
java
public static void addIntegers(List<? super Integer> list) {
list.add(100); // 正确:Integer 是下限的子类
}
此时你可以往列表中添加 Integer 类型的数据,但不能保证读取的是什么类型。
五、泛型类别
泛型根据定义位置和作用范围,可分为泛型类、泛型接口和泛型方法三类。它们的核心都是 "参数化类型",但适用场景和语法略有差异。
泛型类
泛型类是指在类定义时声明类型参数,使得类中的字段、方法参数或返回值可以使用这些类型参数。类型参数的作用域贯穿整个类。
java
public class DataWrapper<T> {
private T data;
public DataWrapper(T data) {
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}

泛型类继承,若父类指定类型,则子类不用指定,默认和父类一样。若父类没有指定泛型类型,则父类、子类都需要默认泛型(Object)

泛型接口
泛型接口与泛型类类似,在接口定义时声明类型参数,实现类需指定具体类型或继续保留泛型。
java
// 定义泛型接口,E表示产出的元素类型
public interface Producer<E> {
E produce();
}
// 实现接口时指定具体类型(如String)
public class StringProducer implements Producer<String> {
@Override
public String produce() {
return "Hello";
}
}
// 实现接口时继续保留泛型(泛型实现类)
public class GenericProducer<T> implements Producer<T> {
private T data;
public GenericProducer(T data) {
this.data = data;
}
@Override
public T produce() {
return data;
}
}
泛型方法
泛型方法是指在方法声明时单独声明类型参数的方法,其类型参数的作用域仅限于当前方法。它可以是静态方法或实例方法,且无需依赖所在类是否为泛型类。
java
public class GenericMethodDemo {
// 泛型实例方法:将输入对象转换为目标类型(简化示例)
public <T> T convert(Object obj, Class<T> clazz) throws Exception {
if (clazz.isInstance(obj)) {
return clazz.cast(obj);
}
// 实际场景可能通过反射、JSON等方式转换
return clazz.getConstructor(String.class).newInstance(obj.toString());
}
// 静态泛型方法:创建指定类型的列表并添加元素
public static <E> List<E> createList(E... elements) {
List<E> list = new ArrayList<>();
for (E e : elements) {
list.add(e);
}
return list;
}
}
// 使用泛型方法
public static void main(String[] args) throws Exception {
GenericMethodDemo demo = new GenericMethodDemo();
// 实例泛型方法:将String转换为Integer
Integer num = demo.convert("123", Integer.class);
// 静态泛型方法:创建String列表
List<String> strList = GenericMethodDemo.createList("a", "b", "c");
}

关键特性
-
泛型方法的类型参数声明(如)必须在返回值前,这是区分泛型方法与普通方法的核心标志;
-
静态方法无法使用所在类的泛型参数(因静态成员属于类,与实例无关),若需泛型需定义为静态泛型方法;
六、泛型的类型表示:Type 接口的实现
在 Java 中,泛型的类型信息在编译后会被部分保留(类型擦除不会完全抹去所有信息),这些信息可通过反射 API 获取。java.lang.reflect.Type接口是所有泛型类型的父接口,其下有 5 种核心实现(或子接口),用于表示不同场景的泛型类型。
Class<?>:非泛型类型
Class是Type的直接实现,用于表示非泛型类型(如String、Integer)或泛型类型的原始类型(如List,未指定类型参数的泛型类)。
java
// String是普通类,类型为Class
Class<String> stringClass = String.class;
// List是泛型类的原始类型,类型仍为Class
Class<List> listClass = List.class;
ParameterizedType:参数化类型
表示指定了具体类型参数的泛型类型,如List、Map<Integer, String>。其核心方法包括:
-
getRawType():获取原始类型(如List的原始类型是List.class);
-
getActualTypeArguments():获取实际类型参数(如List的实际参数是String.class)。
java
public class ParameterizedTypeDemo {
private List<String> stringList;
private Map<Integer, List<String>> complexMap;
public static void main(String[] args) throws NoSuchFieldException {
// 获取stringList字段的类型
Field field1 = ParameterizedTypeDemo.class.getDeclaredField("stringList");
ParameterizedType pt1 = (ParameterizedType) field1.getGenericType();
System.out.println(pt1.getRawType()); // interface java.util.List
System.out.println(pt1.getActualTypeArguments()[0]); // class java.lang.String
// 获取complexMap字段的类型
Field field2 = ParameterizedTypeDemo.class.getDeclaredField("complexMap");
ParameterizedType pt2 = (ParameterizedType) field2.getGenericType();
System.out.println(pt2.getRawType()); // interface java.util.Map
System.out.println(pt2.getActualTypeArguments()[0]); // class java.lang.Integer
System.out.println(pt2.getActualTypeArguments()[1]); // java.util.List<java.lang.String>(也是ParameterizedType)
}
}
TypeVariable:类型变量
表示泛型中的类型参数(如类或方法声明的T、E)。其核心方法包括:
-
getName():获取类型参数名称(如T);
-
getBounds():获取类型参数的上限(如class Box中,T的上限是Number.class)。
java
public class TypeVariableDemo<T extends Number & Serializable, E> {
public static void main(String[] args) {
// 获取类的泛型类型参数
TypeVariable<Class<TypeVariableDemo>>[] typeVariables = TypeVariableDemo.class.getTypeParameters();
// 第一个类型参数T
TypeVariable<Class<TypeVariableDemo>> t = typeVariables[0];
System.out.println(t.getName()); // T
System.out.println(Arrays.toString(t.getBounds())); // [class java.lang.Number, interface java.io.Serializable]
// 第二个类型参数E(无显式上限,默认上限为Object)
TypeVariable<Class<TypeVariableDemo>> e = typeVariables[1];
System.out.println(e.getName()); // E
System.out.println(Arrays.toString(e.getBounds())); // [class java.lang.Object]
}
}
WildcardType:通配符类型
表示泛型通配符(如?、? extends Number、? super Integer)。其核心方法包括:
-
getUpperBounds():获取上限(如? extends Number的上限是Number.class);
-
getLowerBounds():获取下限(如? super Integer的下限是Integer.class)。
java
public class WildcardTypeDemo {
private List<? extends Number> upperList;
private List<? super Integer> lowerList;
public static void main(String[] args) throws NoSuchFieldException {
// 获取上限通配符
Field field1 = WildcardTypeDemo.class.getDeclaredField("upperList");
ParameterizedType pt1 = (ParameterizedType) field1.getGenericType();
WildcardType wt1 = (WildcardType) pt1.getActualTypeArguments()[0];
System.out.println(Arrays.toString(wt1.getUpperBounds())); // [class java.lang.Number]
System.out.println(Arrays.toString(wt1.getLowerBounds())); // [](无下限)
// 获取下限通配符
Field field2 = WildcardTypeDemo.class.getDeclaredField("lowerList");
ParameterizedType pt2 = (ParameterizedType) field2.getGenericType();
WildcardType wt2 = (WildcardType) pt2.getActualTypeArguments()[0];
System.out.println(Arrays.toString(wt2.getUpperBounds())); // [class java.lang.Object](默认上限)
System.out.println(Arrays.toString(wt2.getLowerBounds())); // [class java.lang.Integer]
}
}
为什么需要了解 Type 接口?
在日常开发中,直接使用这些类型的场景较少,但在框架开发(如 Spring、Jackson)中,反射处理泛型是常见需求。例如:
-
Jackson 解析List时,需通过ParameterizedType获取User类型;
-
Spring 的ResolvableType工具类封装了对这些类型的处理,用于依赖注入时的泛型匹配。
七、其他泛型写法
Java 泛型的高级写法主要体现在多边界约束、通配符嵌套、泛型方法重载和递归类型约束等场景。以下是一些实用的高级写法及示例:
多边界类型参数
当需要对泛型类型参数施加多个约束条件时,可以使用 & 符号连接多个接口(或一个类和多个接口)。注意:类必须放在第一个位置,且只能有一个类。
java
// 要求T必须同时实现Serializable和Cloneable接口
public static class Custom<T extends Serializable & Cloneable> {
private T value;
public Custom(T value) {
this.value = value;
}
}
public static class Student implements Serializable, Cloneable {
}
public static void main(String[] args) {
Custom<Student> custom = new Custom<>(new Student());
}
通配符嵌套
在处理复杂泛型结构时,通配符可以嵌套使用,实现更精确的类型约束。
java
// 嵌套通配符示例:表示一个列表,其元素是另一个列表,内层列表的元素类型是Number或其子类
List<? extends List<? extends Number>> nestedList;
// 使用场景:统计嵌套列表中的所有数值之和
public static double sumNestedLists(List<? extends List<? extends Number>> lists) {
double sum = 0;
for (List<? extends Number> innerList : lists) {
for (Number num : innerList) {
sum += num.doubleValue();
}
}
return sum;
}
递归类型约束
允许类型参数引用自身,常用于定义 "可比较自身的类型" 或 "自引用接口"。
java
// 定义一个可排序的元素,要求T必须实现Comparable接口且能比较自身类型
public static class SortedElement<T extends Comparable<T>> {
private T value;
public SortedElement(T value) {
this.value = value;
}
// 判断当前元素是否小于另一个元素
public boolean isLessThan(SortedElement<T> other) {
return this.value.compareTo(other.value) < 0;
}
}
public static void main(String[] args) {
SortedElement<Integer> e1 = new SortedElement<>(10);
SortedElement<Integer> e2 = new SortedElement<>(20);
System.out.println(e1.isLessThan(e2)); // true
}
泛型与函数式接口结合
利用 Java 8+ 的函数式接口和泛型,实现更灵活的类型安全操作。
java
// 定义一个泛型函数式接口,用于转换类型
@FunctionalInterface
public interface Converter<T, R> {
R convert(T source);
}
// 泛型工具类:提供类型转换方法
public static class GenericConverter {
// 使用泛型和函数式接口进行类型转换
public static <T, R> List<R> convertList(List<T> sourceList, Converter<T, R> converter) {
List<R> result = new ArrayList<>();
for (T item : sourceList) {
result.add(converter.convert(item));
}
return result;
}
}
public static void main(String[] args) {
List<String> stringList = Arrays.asList("1", "2", "3");
List<Integer> integerList = GenericConverter.convertList(stringList, Integer::valueOf);
}
泛型数组的创建技巧
由于类型擦除,直接创建泛型数组(如 new T[])是非法的,但可以通过反射创建。
java
public static class GenericArray<T> {
private T[] array;
@SuppressWarnings("unchecked")
public GenericArray(Class<T> clazz, int size) {
// 通过Array.newInstance创建泛型数组
this.array = (T[]) Array.newInstance(clazz, size);
}
public T[] getArray() {
return array;
}
public void set(int index, T value) {
array[index] = value;
}
}
public static void main(String[] args) {
GenericArray<String> stringArray = new GenericArray<>(String.class, 3);
stringArray.set(0, "Hello");
stringArray.set(1, "world");
}
八、注意事项
-
不能使用基本类型作为泛型参数,如 List是非法的,应使用包装类 List。
-
不能创建泛型数组,如 new T[10] 是非法的,因为类型擦除后无法知道实际类型。
-
Java 的泛型是通过类型擦除实现的,也就是说,在运行时,泛型信息会被擦除,所有泛型类型最终都变成其上限(通常是 Object)。
-
不能实例化泛型类型对象,如 new T() 是非法的,因为运行时不知道具体类型。
-
泛型类型参数无法用于 instanceof 检查
-
泛型类的静态成员无法使用类的类型参数
总结
Java 泛型通过 "参数化类型" 实现了类型安全与代码复用,T、E、K、V 等符号是约定俗成的类型参数命名,而?通配符则灵活处理了未知类型的场景。
深入理解泛型类、接口、方法的定义与使用,以及 Type 接口的实现类(如 ParameterizedType、TypeVariable),不仅能写出更健壮的代码,还能为理解框架底层的泛型处理(如反射、类型解析)打下基础。
今日语录:冲锋吧,爷们!