泛型(Generics)是 Java 等编程语言中一种参数化类型的特性,它允许在定义类、接口或方法时,通过类型参数来表示未知类型,直到使用时再指定具体类型。
泛型通俗来讲就是,适用于多种的类型
注意:
泛型不具有继承性,但是数据具有继承性
泛型的 “不可变性”(无继承性)与数据本身 “可继承性” 的对立统一
代码复用
无需为不同类型重复编写逻辑相同的代码。例如,一个泛型容器类List<T>可以存储Integer、String等任意类型,而无需分别定义IntList、StringList。
类型安全
编译时就会检查类型匹配,避免运行时出现ClassCastException。例如,向List<Integer>中添加String会直接编译报错,而不是在运行时崩溃。
消除强制类型转换
使用泛型后,从容器中获取元素时无需手动强转。例如:
// 非泛型(需要强转)
List list = new ArrayList();
list.add(10);
Integer num = (Integer) list.get(0); // 必须强转
// 泛型(无需强转)
List<Integer> list = new ArrayList<>();
list.add(10);
Integer num = list.get(0); // 直接获取Integer类型在类定义时声明类型参数,作用于整个类的成员变量、方法参数和返回值。
语法:class 类名<类型参数列表> { ... }
// 多类型参数:T代表键,V代表值
class Pair<T, V> {
private T key;
private V value;
public Pair(T key, V value) {
this.key = key;
this.value = value;
}
public T getKey() { return key; }
public V getValue() { return value; }
}
// 使用:指定T为String,V为Integer
Pair<String, Integer> pair = new Pair<>("age", 20);
String key = pair.getKey(); // String类型
Integer value = pair.getValue(); // Integer类型与泛型类类似,在接口定义时声明类型参数,实现类需指定具体类型或继续保留泛型。
语法:interface 接口名<类型参数列表> { ... }
// 泛型接口:定义“可比较”的行为
interface Comparable<T> {
int compareTo(T other); // 比较当前对象与另一个T类型对象
}
// 实现类:指定T为Integer
class IntegerComparable implements Comparable<Integer> {
private int num;
@Override
public int compareTo(Integer other) {
return Integer.compare(num, other);
}
}泛型接口实现的两种方法:
1.实现类给出具体的类型
// 泛型接口
interface Repository<T> {
void save(T data);
T findById(int id);
}
// 实现时直接指定具体类型(这里是 String)
class StringRepository implements Repository<String> {
@Override
public void save(String data) {
System.out.println("保存字符串:" + data);
}
@Override
public String findById(int id) {
return "固定字符串"; // 简单演示
}
}
public class Demo1 {
public static void main(String[] args) {
Repository<String> repo = new StringRepository();
repo.save("Hello Generics");
System.out.println(repo.findById(1));
}
}2.实现类继续延续泛型,创建实现类对象再确定类型
// 泛型接口
interface Repository<T> {
void save(T data);
T findById(int id);
}
// 实现类仍然带泛型参数
class MemoryRepository<T> implements Repository<T> {
@Override
public void save(T data) {
System.out.println("保存数据:" + data);
}
@Override
public T findById(int id) {
return null; // 简单演示
}
}
public class Demo2 {
public static void main(String[] args) {
// 这里才决定类型
Repository<Integer> intRepo = new MemoryRepository<>();
intRepo.save(100);
Repository<Double> doubleRepo = new MemoryRepository<>();
doubleRepo.save(3.14);
}
}3. 泛型方法(Generic Method)
在单个方法中声明类型参数,作用范围仅限当前方法,与类是否泛型无关。
语法:修饰符 <类型参数列表> 返回值类型 方法名(参数列表) { ... }
class ArrayUtils {
// 泛型方法:交换数组中两个位置的元素
public static <T> void swap(T[] array, int i, int j) {
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
// 使用:无需定义泛型类,直接调用泛型方法
String[] strArray = {"A", "B", "C"};
ArrayUtils.swap(strArray, 0, 2); // 自动适配String类型
Integer[] intArray = {1, 2, 3};
ArrayUtils.swap(intArray, 0, 2); // 自动适配Integer类型泛型允许通过extends或super限制类型参数的范围,避免 “无限制的泛型” 导致的功能局限。
extends)限制类型参数必须是指定类的子类或指定接口的实现类。
语法:<T extends 上界类型>
&连接)// T必须是Number的子类,且实现Comparable接口
class Calculator<T extends Number & Comparable<T>> {
public T max(T a, T b) {
// 因T实现了Comparable,可调用compareTo方法
return a.compareTo(b) >= 0 ? a : b;
}
public double sum(T a, T b) {
// 因T是Number子类,可调用doubleValue方法
return a.doubleValue() + b.doubleValue();
}
}
// 合法:Integer是Number子类且实现Comparable
Calculator<Integer> intCalc = new Calculator<>();
intCalc.max(10, 20); // 正确
intCalc.sum(10.5, 20.3); // 错误:Double类型与Integer不匹配
// 非法:String不是Number子类
Calculator<String> strCalc = new Calculator<>(); // 编译报错super)限制类型参数必须是指定类的父类(包括自身)。
语法:<T super 下界类型>
通常用于通配符(? super T),限制写入操作的类型范围。
// 向集合中添加Fruit及其子类(如Apple)
public static void addFruits(List<? super Fruit> list) {
list.add(new Apple()); // 合法:Apple是Fruit子类
list.add(new Fruit()); // 合法:Fruit是自身
// list.add(new Food()); // 错误:Food是Fruit父类,超出下界
}通配符 ? 用于表示 “未知类型”,配合extends和super实现灵活的泛型适配,解决泛型的 “不可变性” 问题(如List<Apple>不是List<Fruit>的子类)。
?)表示任意类型,适用于 “只读取不修改” 且不依赖具体类型的场景。
// 打印任意类型集合的元素
public static void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
// 可接收List<String>、List<Integer>等任意泛型集合
printList(Arrays.asList("A", "B"));
printList(Arrays.asList(1, 2, 3));? extends T)表示 “T 及其子类”,适用于 “读取为主” 的场景(能安全获取 T 类型数据,但限制写入)。
// 计算水果重量总和(Fruit及其子类都有weight属性)
public static double sumWeight(List<? extends Fruit> fruits) {
double total = 0;
for (Fruit f : fruits) { // 可安全转型为Fruit
total += f.getWeight();
}
return total;
}
// 合法:List<Apple>、List<Banana>都是Fruit的子类集合
sumWeight(Arrays.asList(new Apple(), new Banana()));? super T)表示 “T 及其父类”,适用于 “写入为主” 的场景(能安全写入 T 类型数据,但读取时只能作为 Object)
// 向集合中添加整数(Integer及其父类集合均可接收)
public static void addIntegers(List<? super Integer> list) {
list.add(10); // 合法:Integer是自身
list.add(20);
}
// 合法:List<Integer>、List<Number>、List<Object>均可接收
addIntegers(new ArrayList<Integer>());
addIntegers(new ArrayList<Number>());Java 泛型是编译时特性,编译后会将泛型信息 “擦除”,仅保留原始类型(Raw Type),运行时不包含泛型参数。
无约束的泛型(如<T>)擦除为Object;
有上界的泛型(如<T extends Number>)擦除为上界类型(Number)。
// 编译前:泛型类
class Box<T> {
private T value;
public T getValue() { return value; }
}
// 编译后:类型擦除为Object
class Box {
private Object value;
public Object getValue() { return value; }
}影响:
运行时无法获取泛型参数的具体类型(如list.getClass()返回ArrayList,而非ArrayList<String>);
不能创建泛型数组(如new T[10]不允许,需通过(T[]) new Object[10]间接实现)。
无法在运行时获取泛型参数类型:如list.getClass()返回ArrayList,而非ArrayList<String>;
不能创建泛型数组:new T[10]编译报错(需通过(T[]) new Object[10]间接实现);
泛型参数不能用于instanceof判断:if (obj instanceof List<String>)编译报错;
可能破坏多态性:子类重写泛型方法时,擦除后的方法签名可能与父类不一致(需桥接方法解决)。
类型擦除可能导致子类重写的泛型方法与父类方法签名不一致,从而破坏多态性。桥接方法是编译器自动生成的合成方法,用于修复这种签名冲突,保证多态性正常工作。
// 父类:泛型接口
interface Comparable<T> {
int compareTo(T o);
}
// 子类:实现接口并指定T为Integer
class IntegerComparable implements Comparable<Integer> {
@Override
public int compareTo(Integer o) { // 重写方法,参数为Integer
return 0;
}
}类型擦除后:
Comparable<T>擦除为Comparable,方法变为int compareTo(Object o);IntegerComparable的compareTo(Integer o)与父类擦除后的方法签名不一致(参数类型不同)。此时,若没有桥接方法,IntegerComparable实际上并未重写父类的compareTo方法,多态性会失效。
class IntegerComparable implements Comparable {
// 1. 程序员编写的方法(参数为Integer)
public int compareTo(Integer o) {
return 0;
}
// 2. 编译器自动生成的桥接方法(参数为Object,匹配父类擦除后的签名)
public int compareTo(Object o) {
// 调用实际的compareTo方法,并进行类型转换
return compareTo((Integer) o);
}
}编译器会为IntegerComparable自动生成一个桥接方法,确保与父类擦除后的方法签名一致:
Object(父类擦除后的类型);合成方法(synthetic method),在字节码中标记为ACC_SYNTHETIC,通常不会在源码中显示。//这里是博主找的资料去了解的,反射还没有学
通过反射可以查看类中是否存在桥接方法:
import java.lang.reflect.Method;
public class TestBridge {
public static void main(String[] args) {
Method[] methods = IntegerComparable.class.getDeclaredMethods();
for (Method m : methods) {
System.out.println("方法名:" + m.getName() +
",参数类型:" + m.getParameterTypes()[0].getSimpleName() +
",是否桥接方法:" + m.isBridge());
}
}
}
// 输出:
// 方法名:compareTo,参数类型:Integer,是否桥接方法:false
// 方法名:compareTo,参数类型:Object,是否桥接方法:trueClassCastException(如向List<Integer>中添加String会直接报错)。ArrayList<T>可存储任意类型,无需为每个类型单独定义集合)。list.get(0)直接返回String,而非Object)。T extends Number可安全调用doubleValue())。List<Apple>不是List<Fruit>的子类,即使Apple是Fruit的子类(需用通配符? extends Fruit解决)。
List<int>错误,应改为List<Integer>)。
static T value;错误)。
在 Java 泛型中,约束不仅可以是一个类,还可以同时约束多个接口,甚至可以是 “一个类 + 多个接口” 的组合约束。具体规则如下:
& 连接多个约束泛型参数的多约束通过 & 符号连接,格式为:
<T extends 类名 & 接口1 & 接口2 & ...>注意:
// T必须同时实现Comparable和Serializable接口
class MyClass<T extends Comparable<T> & java.io.Serializable> {
public void method(T t) {
t.compareTo(t); // 可调用Comparable接口方法
// 可进行序列化操作(因实现了Serializable)
}
}// T必须是Number的子类,且同时实现Comparable和Cloneable接口
class DataProcessor<T extends Number & Comparable<T> & Cloneable> {
public void process(T data) {
data.doubleValue(); // 调用Number类的方法
data.compareTo(data); // 调用Comparable接口方法
try {
data.clone(); // 调用Cloneable接口相关方法
} catch (CloneNotSupportedException e) {
// 处理异常
}
}
}Number 在 & 之前);& 连接,无顺序要求(但通常按逻辑排序);T 必须满足所有约束(既是指定类的子类,又实现了所有指定接口)。// 合法:Integer是Number子类,且实现了Comparable<Integer>和Cloneable
DataProcessor<Integer> intProcessor = new DataProcessor<>();
// 合法:Double符合所有约束
DataProcessor<Double> doubleProcessor = new DataProcessor<>();
// 非法:String不是Number子类,不满足类约束
DataProcessor<String> strProcessor = new DataProcessor<>(); // 编译报错
// 非法:Number未实现Comparable接口(需具体子类实现)
DataProcessor<Number> numProcessor = new DataProcessor<>(); // 编译报错在 Java 中,“多重泛型” 通常指一个泛型类 / 接口 / 方法声明多个类型参数(即包含多个泛型变量),这些参数可以独立约束、独立使用,用于处理更复杂的多类型关联场景。
例如class Pair<K, V>中,K和V就是两个独立的泛型参数,分别代表 “键” 和 “值” 的类型,这种多参数设计是实现Map等数据结构的基础。
声明多个泛型参数时,用逗号分隔,格式为:
// 泛型类:多个类型参数用逗号分隔
class 类名<T1, T2, ..., Tn> { ... }
// 泛型接口
interface 接口名<T1, T2, ..., Tn> { ... }
// 泛型方法
修饰符 <T1, T2, ..., Tn> 返回值类型 方法名(参数列表) { ... }注意:每个类型参数可以单独设置约束(如extends),彼此独立。
每个泛型参数可以单独设置约束(extends),实现更精确的类型限制。
// 三个泛型参数,分别带不同约束:
// T:必须是Number子类(数值类型)
// U:必须实现Comparable接口(可比较类型)
// V:无约束(任意类型)
class DataHandler<T extends Number, U extends Comparable<U>, V> {
// 方法1:将数值类型T转换为字符串
public String convertNumber(T num) {
return String.valueOf(num);
}
// 方法2:比较两个U类型对象的大小
public U getMax(U a, U b) {
return a.compareTo(b) >= 0 ? a : b;
}
// 方法3:存储任意类型V的数据
public void storeData(V data) {
System.out.println("存储数据:" + data);
}
}
// 使用示例
public class TestDataHandler {
public static void main(String[] args) {
// 实例化:
// T=Double(Number子类),U=String(实现了Comparable),V=Boolean(任意类型)
DataHandler<Double, String, Boolean> handler = new DataHandler<>();
// 调用方法:自动匹配类型
String numStr = handler.convertNumber(3.14); // Double→String
String maxStr = handler.getMax("apple", "banana"); // 比较字符串
handler.storeData(true); // 存储Boolean
System.out.println(numStr); // 输出:3.14
System.out.println(maxStr); // 输出:banana(按字典序"banana" > "apple")
}
}泛型方法也可以声明多个类型参数,用于处理方法级别的多类型逻辑。
class ArrayConverter {
// 泛型方法:声明两个参数T(源类型)和U(目标类型)
// 功能:将T类型数组转换为U类型数组(通过转换器函数)
public static <T, U> U[] convert(T[] source, Converter<T, U> converter) {
U[] result = (U[]) new Object[source.length];
for (int i = 0; i < source.length; i++) {
result[i] = converter.convert(source[i]);
}
return result;
}
}
// 转换器接口(也使用多重泛型)
interface Converter<T, U> {
U convert(T source);
}
// 使用示例
public class TestConverter {
public static void main(String[] args) {
// 1. 将Integer数组转换为String数组(如100 → "100元")
Integer[] numbers = {100, 200, 300};
String[] strs = ArrayConverter.convert(numbers, n -> n + "元");
// 输出:[100元, 200元, 300元]
// 2. 将String数组转换为Integer数组(如"10" → 10)
String[] numberStrs = {"10", "20", "30"};
Integer[] ints = ArrayConverter.convert(numberStrs, Integer::parseInt);
// 输出:[10, 20, 30]
}
}参数独立性:每个泛型参数(如K、V)是独立的,类型可以不同(如K=String、V=Integer),也可以相同(如K=V=String)。
约束独立性:每个参数可单独设置约束,互不影响。例如:
// T必须是Number子类,U必须实现Serializable,V无约束
class Example<T extends Number, U extends java.io.Serializable, V> { ... }类型安全强化:相比单泛型参数,多重泛型能更精确地描述多类型之间的关联(如 “键 - 值”“源 - 目标”),编译时会分别检查每个参数的类型匹配。
代码复用最大化:一套逻辑可适配多种类型组合,例如Pair<K, V>既可以处理 “字符串 - 整数”,也可以处理 “整数 - 对象” 等任意组合。
Pair<K, V>声明为Pair<String, Integer>,则第一个参数必须是K的类型,第二个是V的类型。// 接收"键为任意类型,值为Number子类"的Pair
void processPair(Pair<?, ? extends Number> pair) { ... }