一、什么是 Java 泛型?
泛型(Generics) 是 Java 中一种强大的编程机制,允许在定义类、接口和方法时使用类型参数。通过泛型,可以将数据类型作为参数传递,从而实现代码的通用性和类型安全。
简单来说,泛型让你可以编写更灵活、更通用的代码,同时避免类型转换错误。例如:
// 使用泛型的 List
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
String str = stringList.get(0); // 不需要强制类型转换
如果没有泛型,List
默认存储的是 Object
类型的对象,取出时需要手动进行类型转换,容易出错。
二、泛型的底层原理
Java 泛型的底层实现是基于 类型擦除(Type Erasure) 的。所谓类型擦除,是指编译器在编译阶段会将泛型类型替换为它们的上界(通常是 Object
),并在运行时移除泛型信息。这样做的目的是为了兼容旧版本的 Java(Java 5 引入泛型之前)。
示例:
List<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0);
经过编译后,实际上是这样的:
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0); // 编译器插入了类型转换
因此,泛型在运行时并不存在具体的类型信息,所有泛型相关的操作都是在编译时完成的。
类型擦除的优点和缺点:
- 优点 :
- 向下兼容旧版本的 Java。
- 减少了运行时的性能开销。
- 缺点 :
- 泛型类型信息在运行时不可用。
- 无法创建泛型类型的实例(如
new T()
)。
三、为什么要使用泛型?
-
类型安全 :
泛型可以在编译时检查类型,避免运行时的
ClassCastException
错误。例如,如果你试图向一个List<String>
中添加一个Integer
,编译器会直接报错。 -
减少类型转换 :
使用泛型后,编译器会自动插入必要的类型转换代码,减少了手动类型转换的麻烦。
-
提高代码复用性 :
泛型可以让代码更加通用,适用于多种数据类型。例如,
ArrayList<T>
可以用于存储任何类型的对象。 -
增强可读性 :
使用泛型后,代码的意图更加明确,例如
List<String>
明确表示这是一个存储字符串的列表。
四、泛型的应用场景
以下是泛型在 Java 中的主要应用场景:
1. 集合类
这是泛型最常见的应用场景。Java 集合框架(如 List
、Set
、Map
等)都支持泛型,使得集合可以存储特定类型的元素。
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
for (String name : names) {
System.out.println(name);
}
2. 自定义泛型类
你可以定义自己的泛型类,使其能够处理多种类型的数据。
public class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
// 使用
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
String value = stringBox.getItem();
3. 泛型方法
除了泛型类,你还可以定义泛型方法,使方法能够处理多种类型的参数。
public <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
// 使用
printArray(new Integer[]{1, 2, 3});
printArray(new String[]{"A", "B", "C"});
4. 泛型接口
接口也可以定义为泛型,使其实现类能够指定具体类型。
public interface Container<T> {
void add(T item);
T get(int index);
}
public class MyContainer<T> implements Container<T> {
private List<T> items = new ArrayList<>();
@Override
public void add(T item) {
items.add(item);
}
@Override
public T get(int index) {
return items.get(index);
}
}
5. 通配符(Wildcard)详细说明
泛型中的通配符(?
)用于表示未知类型,通常用于方法参数或返回值。
-
无界通配符 :
<?>
表示任意类型。 -
上界通配符 :
<? extends T>
表示 T 或其子类。 -
下界通配符 :
<? super T>
表示 T 或其父类。public void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}public void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
}
6. 泛型与反射
虽然泛型在运行时会被擦除,但可以通过反射获取原始类型信息。
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Class<?> clazz = list.getClass();
System.out.println(clazz); // 输出:class java.util.ArrayList
}
}
7. 泛型在设计模式中的应用
许多设计模式(如工厂模式、策略模式等)都可以结合泛型来实现更灵活的设计。
public interface Factory<T> {
T create();
}
public class StringFactory implements Factory<String> {
@Override
public String create() {
return "Hello";
}
}
