Java泛型(Generics)是Java SE 5
引入的一项核心语言功能,是一种用于在编译时
对类型进行检查的技术,它允许程序员在编写类
、接口
和方法
时使用类型参数(type parameters),而不是硬编码具体的类型。
这样做可以让代码更加灵活和可复用,同时增强了类型安全性,能够在编译阶段捕获类型不匹配的错误,减少运行时错误。
基础概念
- 类型参数 :在定义泛型类或方法时,可以使用尖括号
< >
中的类型参数表示待定的类型。- 例如,
List<T>
中的T
就是一个类型参数,当你创建一个List
实例时,可以指定T
为任何有效类型,如List<String>
或List<Integer>
。
- 例如,
- 类型约束 :类型参数可以带有约束条件,表明它必须是某种特定类型或者其子类型。
- 例如,
T extends Comparable<T>
表示T
必须是实现了Comparable<T>
接口的类型。
- 例如,
- 泛型方法:不只是类可以使用泛型,方法也可以拥有自己的类型参数,使方法独立于类的泛型类型。
优势
- 类型安全 :通过在编译时就进行类型检查,可以防止插入错误类型的元素,从而降低运行时出现
ClassCastException
的风险。 - 代码复用:通过泛型,可以编写一个通用的类或方法,适用于多种数据类型,避免为每种类型都编写重复的代码。
定义格式
泛型的定义格式主要包括在类、接口、方法和数组中定义泛型类型参数。
定义泛型类
修饰符 class 类名<类型> { }
:泛型类通过在其名称后面使用尖括号< >
内指定类型参数来定义。例如,定义一个名为Box
的泛型类,其中的T
表示任意类型:
java
// 定义一个泛型类
public class Box<T> {
private T item;
public Box(T item) {
this.item = item;
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
// 使用泛型类
Box<String> stringBox = new Box<>("Hello");
Box<Integer> integerBox = new Box<>(123);
定义泛型接口
修饰符 interface 接口名<类型> { }
:泛型接口的定义方式与泛型类类似,同样在接口名称后使用尖括号< >
指定类型参数:
java
// 定义一个泛型接口
public interface Generator<T> {
T generate();
}
// 实现泛型接口
public class StringGenerator implements Generator<String> {
@Override
public String generate() {
return "Generated String";
}
}
定义泛型方法
修饰符 <类型> 返回值类型 方法名(类型 变量名) { }
:泛型方法是在方法签名中声明类型参数,不受类级别的泛型参数影响:
java
public class Util {
public static <T> void printArray(T[] array) {
for (T item : array) {
System.out.println(item);
}
}
}
// 使用泛型方法
Integer[] integers = {1, 2, 3};
Util.printArray(integers);
String[] strings = {"Hello", "World"};
Util.printArray(strings);
泛型类型的边界(类型约束)
- 可以通过
extends
或super
关键字来指定类型参数的边界,限制它可以是某种类型或其子类/父类:
java
public class ComparableBox<T extends Comparable<T>> {
private T item;
// ... methods ...
public int compare(T other) {
return item.compareTo(other);
}
}
// 使用带边界约束的泛型类
ComparableBox<String> comparableStringBox = new ComparableBox<>("abc");
通配符类型
<?>
:通配符类型用于更灵活的类型匹配,主要有两种形式:?
表示未知类型;? extends SomeType
表示某种类型的子类型;? super SomeType
表示某种类型的父类型;
java
public void processElements(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
// 使用通配符类型的泛型方法
List<String> stringList = new ArrayList<>();
processElements(stringList);
List<Integer> integerList = new ArrayList<>();
processElements(integerList);
注意事项
- 泛型只能用于引用类型,不能用于基本数据类型。例如,不能有
List<int>
,而应该使用List<Integer>
。 - 泛型类或接口不能有泛型类型的实例字段,因为这样的字段在运行时无法确定具体类型。
- 泛型的类型擦除:Java泛型在编译时提供类型检查,在运行时不保留泛型类型信息。这意味着泛型类型信息在运行时会被擦除。