一、什么是 Java 泛型?
泛型(Generics)是 Java 1.5 引入的一项重要特性,主要用于 类型参数化 ,允许在类、接口和方法定义时使用 类型参数 (Type Parameter),从而提高代码的复用性 、类型安全性 和可读性。
二、为什么需要泛型?
1. 解决类型安全问题
在没有泛型的时代,集合(如 ArrayList
)存储的是 Object
类型,容易发生类型转换异常。
java
import java.util.ArrayList;
public class WithoutGenerics {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 没有指定类型
list.add("Hello");
list.add(100); // 这里插入了一个整数
for (Object obj : list) {
// 需要强制转换,否则无法使用 String 方法
String str = (String) obj;
System.out.println(str.toUpperCase()); // 运行时可能报 ClassCastException
}
}
}
运行时会抛出 ClassCastException
,因为 100
不能转换为 String
。
2. 使用泛型后,编译时即可发现错误
java
import java.util.ArrayList;
public class WithGenerics {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>(); // 指定类型为 String
list.add("Hello");
// list.add(100); // 编译时直接报错,避免了运行时异常
for (String str : list) {
System.out.println(str.toUpperCase());
}
}
}
- 类型安全 :只能存储
String
,避免了ClassCastException
。 - 可读性好:不需要强制类型转换。
三、泛型的基本用法
1. 泛型类
泛型类在定义时,使用 类型参数(T) 来表示类中可以使用的类型。
java
// 定义一个泛型类
class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
public class GenericClassExample {
public static void main(String[] args) {
Box<String> stringBox = new Box<>(); // 指定 T 为 String
stringBox.setItem("Hello");
System.out.println(stringBox.getItem());
Box<Integer> intBox = new Box<>(); // 指定 T 为 Integer
intBox.setItem(100);
System.out.println(intBox.getItem());
}
}
类型参数常见约定:
T
(Type):表示任意类类型E
(Element):集合中的元素类型K
(Key):键V
(Value):值
2. 泛型方法
泛型方法可以在 普通类或泛型类 中定义,方法的泛型参数只在方法内部生效。
java
class Util {
// 泛型方法
public static <T> void print(T data) {
System.out.println(data);
}
}
public class GenericMethodExample {
public static void main(String[] args) {
Util.print("Hello");
Util.print(123);
Util.print(45.6);
}
}
- 需要声明在方法返回值之前
- 泛型方法可以独立于类的泛型类型
3. 泛型接口
泛型接口通常用于 定义通用的操作,如数据存储、服务层 API。
java
// 定义泛型接口
interface Repository<T> {
void save(T data);
T get();
}
// 实现泛型接口
class StringRepository implements Repository<String> {
private String data;
@Override
public void save(String data) {
this.data = data;
}
@Override
public String get() {
return data;
}
}
public class GenericInterfaceExample {
public static void main(String[] args) {
Repository<String> repo = new StringRepository();
repo.save("Hello");
System.out.println(repo.get());
}
}
也可以使用 泛型类 实现泛型接口:
java
class GenericRepository<T> implements Repository<T> {
private T data;
@Override
public void save(T data) {
this.data = data;
}
@Override
public T get() {
return data;
}
}
四、泛型的高级用法
1. 泛型的通配符
通配符 ? 代表不确定的类型,主要用于:
- 限定方法参数,使其接受不同的泛型类型。
- 保证类型安全,避免强制转换。
(1)? extends T 上界通配符
限制为 T
及其子类,适用于只 读取数据 的场景。
java
import java.util.List;
public class WildcardExample {
public static void printNumbers(List<? extends Number> list) {
for (Number n : list) {
System.out.println(n);
}
}
public static void main(String[] args) {
List<Integer> intList = List.of(1, 2, 3);
List<Double> doubleList = List.of(1.1, 2.2, 3.3);
printNumbers(intList);
printNumbers(doubleList);
}
}
- 允许
Integer
和Double
作为Number
的子类传入。 - 不能往 list 里添加元素 ,否则会引发
编译错误
,因为? extends Number
可能是Integer
或Double
,不确定具体类型。
(2)? super T 下界通配符
限制为 T
及其 父类 ,适用于 写入数据 的场景。
java
import java.util.List;
import java.util.ArrayList;
public class SuperWildcardExample {
public static void addNumbers(List<? super Integer> list) {
list.add(10);
list.add(20);
}
public static void main(String[] args) {
List<Number> numberList = new ArrayList<>();
addNumbers(numberList);
System.out.println(numberList);
}
}
- 允许
Integer
及其父类(如Number
)的List
作为参数。 - 可以往 list 里添加 Integer 类型的元素。
2. 泛型擦除(Type Erasure)
Java 的泛型是 编译时 作用的,编译后会进行 类型擦除 ,即所有泛型参数都会被 替换为 Object(或其上界类型)。
java
class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
编译后等价于:
java
class Box {
private Object item;
public void setItem(Object item) {
this.item = item;
}
public Object getItem() {
return item;
}
}
影响:
- 不能直接创建泛型数组 :
T[] array = new T[10]; // 编译错误
- 不能使用 instanceof 检测泛型类型 :
if (obj instanceof Box<String>) // 编译错误
总结
特性 | 说明 |
---|---|
泛型类 | 通过 <T> 定义,可以使用不同类型创建实例 |
泛型方法 | 方法独立于类的泛型,可以使用不同类型参数 |
泛型接口 | 定义通用接口,支持泛型类实现 |
通配符 ? |
? extends T 适用于读取,? super T 适用于写入 |
泛型擦除 | 编译后泛型类型被擦除,限制了某些操作 |
注🚀:泛型是 Java 类型安全 和 代码复用 的重要特性,熟练掌握可以大幅提升开发效率!