在 Java 中,泛型(Generics) 是一种允许在定义类、接口或方法时使用类型参数的特性。它的核心目的是增强类型安全性、减少类型转换错误,并提高代码复用性。下面通过关键概念和示例详细解释:
一、泛型的核心作用
- 类型安全 :编译时检查类型错误,避免运行时
ClassCastException
- 消除强制类型转换:代码更简洁
- 代码复用:一套逻辑可处理多种数据类型
二、基本语法
1. 泛型类
java
// T 是类型参数(可自定义名称)
public class Box<T> {
private T content;
public void setContent(T content) {
this.content = content;
}
public T getContent() {
return content;
}
}
使用示例:
java
Box<String> stringBox = new Box<>();
stringBox.setContent("Hello");
String value = stringBox.getContent(); // 无需强制转换
Box<Integer> intBox = new Box<>();
intBox.setContent(100);
2. 泛型方法
java
public <T> void printArray(T[] array) {
for (T item : array) {
System.out.print(item + " ");
}
}
使用示例:
java
String[] strs = {"A", "B", "C"};
printArray(strs); // 自动推断 T 为 String
Integer[] ints = {1, 2, 3};
printArray(ints); // T 为 Integer
3. 泛型接口
java
public interface Repository<T> {
void save(T entity);
T findById(int id);
}
实现示例:
java
public class UserRepository implements Repository<User> {
@Override
public void save(User user) { /* ... */ }
@Override
public User findById(int id) { /* ... */ }
}
三、类型限定(边界)
1. 上界通配符 <? extends T>
- 接受
T
或其子类型
java
// 只能读取数据(安全)
public double sum(List<? extends Number> list) {
double total = 0;
for (Number num : list) {
total += num.doubleValue();
}
return total;
}
使用:
java
List<Integer> ints = List.of(1, 2, 3);
sum(ints); // ✅ Integer 是 Number 的子类
2. 下界通配符 <? super T>
- 接受
T
或其父类型
java
// 可写入数据(安全)
public void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 5; i++) {
list.add(i);
}
}
使用:
java
List<Number> numbers = new ArrayList<>();
addNumbers(numbers); // ✅ Number 是 Integer 的父类
3. 类型参数边界
java
// 要求 T 必须实现 Comparable 接口
public <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
四、类型擦除(重要机制)
Java 泛型在编译后会被擦除,转换为原始类型:
- 泛型类
Box<T>
→ 运行时变为Box
(原始类型) - 类型参数
T
→ 替换为Object
或边界类型(如T extends Number
则替换为Number
) - 编译器自动插入强制类型转换
示例:
java
// 编译前
Box<String> box = new Box<>();
String s = box.getContent();
// 编译后(等效代码)
Box box = new Box();
String s = (String) box.getContent(); // 编译器添加的类型转换
五、使用限制
-
不能实例化泛型类型
javaT obj = new T(); // ❌ 编译错误
-
不能创建泛型数组
javaT[] array = new T[10]; // ❌ 错误
-
静态成员不能使用类型参数
javaprivate static T staticVar; // ❌ 错误
-
基本类型不可作为类型参数
java// Box<int> box = new Box<>(); ❌ 错误 Box<Integer> box = new Box<>(); // ✅ 使用包装类
六、最佳实践
-
优先使用泛型方法:当方法独立于类泛化时更灵活
-
使用
List
而非数组:避免泛型数组问题 -
合理使用通配符 :
Producer-Extends, Consumer-Super
(PECS原则)
-
避免原生类型 :
javaList list = new ArrayList(); // ❌ 原生类型(有警告) List<String> list = new ArrayList<>(); // ✅
七、完整示例
java
// 泛型类 + 类型限定
public class Container<T extends Number> {
private T value;
public Container(T value) {
this.value = value;
}
// 泛型方法
public <U extends Number> boolean isEqual(U other) {
return this.value.doubleValue() == other.doubleValue();
}
}
// 使用
Container<Double> container = new Container<>(3.14);
boolean result = container.isEqual(3.14f); // ✅ 比较 Double 和 Float
通过泛型,Java 实现了类型安全的通用编程,显著提升了代码质量和可维护性。