Java 泛型详解
在 Java 的类型系统中,泛型(Generics) 是一个非常重要的特性。它让我们能够编写更通用、更安全的代码,尤其是在处理集合类(如 List
、Map
等)时,泛型的使用可以大大减少类型转换的麻烦,并在编译阶段捕获类型错误。本文将带你系统地了解 Java 泛型的概念、语法、使用方式、边界、通配符和原理。
一、什么是泛型?
泛型就是参数化类型。所谓参数化类型,就是将类型参数化,就像方法可以传入不同的参数一样,类和方法也可以传入“类型参数”。这样我们可以用同一份代码操作多种类型,而不必为了支持不同类型写多个重复的代码逻辑。
举个例子:
// 没有泛型的写法
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0); // 需要强制类型转换// 使用泛型
List<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0); // 不需要强制转换,类型安全
二、泛型的语法
1. 泛型类
public class Box<T> {private T content;public void setContent(T content) {this.content = content;}public T getContent() {return content;}
}
使用时:
Box<String> stringBox = new Box<>();
stringBox.setContent("泛型示例");
String content = stringBox.getContent();
2. 泛型方法
public class Utils {public static <T> void printArray(T[] array) {for (T element : array) {System.out.println(element);}}
}
调用:
String[] strings = {"Java", "C++", "Python"};
Utils.printArray(strings);
3. 泛型接口
public interface Generator<T> {T next();
}
实现类:
public class StringGenerator implements Generator<String> {public String next() {return "Hello";}
}
三、泛型的边界:extends 和 super
1. 上界通配符(<? extends T>
)
表示参数类型是 T 或 T 的子类。常用于读取数据时:
public void print(List<? extends Number> list) {for (Number n : list) {System.out.println(n);}
}
你可以传入 List<Integer>
、List<Double>
等。
2. 下界通配符(<? super T>
)
表示参数类型是 T 或 T 的父类。常用于写入数据时:
public void add(List<? super Integer> list) {list.add(10); // 可以添加 Integer 或其子类
}
你可以传入 List<Object>
、List<Number>
等。
四、通配符 <?>
的使用
<?>
表示不确定的类型,它是所有泛型的父类。适用于只读的场景,例如:
public void printList(List<?> list) {for (Object obj : list) {System.out.println(obj);}
}
你不能向 List<?>
添加任何元素(除了 null),因为编译器无法确定类型安全。
五、类型擦除与泛型的本质
Java 的泛型采用**类型擦除机制(Type Erasure)**来实现:编译器在编译阶段会将泛型信息擦除,在字节码中不保留泛型信息。
举个例子:
Box<String> stringBox = new Box<>();
Box<Integer> intBox = new Box<>();
在编译后,这两种类型都会变成原始类型 Box
。所以在运行时:
stringBox.getClass() == intBox.getClass(); // true
这也就是为什么 Java 中不能使用泛型数组的原因:
List<String>[] listArray = new List<String>[10]; // 编译错误
六、泛型的限制与注意事项
-
不能使用基本类型作为泛型参数:
Box<int> box = new Box<>(); // 编译错误 // 使用包装类代替 Box<Integer> box = new Box<>();
-
不能创建泛型数组:
T[] array = new T[10]; // 编译错误
-
不能实例化泛型类型参数:
T obj = new T(); // 编译错误
-
静态方法中不能引用类的泛型参数:
泛型参数是实例级别的,静态方法不属于实例。
七、泛型的实际应用场景
-
集合类(Collections Framework):
所有集合类如List<T>
、Map<K, V>
都使用了泛型。 -
通用工具类:
例如Collections.sort(List<T> list)
使用泛型做排序。 -
DAO 层的抽象类或接口:
泛型使得我们可以编写通用的数据访问逻辑,如:public interface BaseDao<T> {T findById(int id);void save(T entity); }
八、泛型的高级技巧
1. 多个泛型参数
public class Pair<K, V> {private K key;private V value;// 省略 getter/setter
}
2. 泛型限定符(bounded type parameter)
public <T extends Comparable<T>> T max(T a, T b) {return a.compareTo(b) > 0 ? a : b;
}
3. 捕获通配符
public void swap(List<?> list, int i, int j) {swapHelper(list, i, j);
}private <T> void swapHelper(List<T> list, int i, int j) {T temp = list.get(i);list.set(i, list.get(j));list.set(j, temp);
}
总结
泛型是 Java 提供的强大特性,它的核心目标是提升代码的重用性与类型安全性。通过泛型,我们可以:
-
编写可复用的类、接口和方法;
-
编译期检查类型安全;
-
减少类型转换的代码量。
掌握泛型,对于写出优雅、高质量的 Java 代码至关重要。希望本文能帮助你全面理解泛型的原理与实践!