在Java 5之前,集合框架的设计存在一个明显的痛点:所有集合类都只能存储Object类型。这意味着开发者在使用集合时,不得不频繁地进行类型强制转换。例如:
java复制List list = new ArrayList();
list.add("Hello");
String str = (String) list.get(0); // 必须强制转换
这种设计带来了三个主要问题:
泛型的引入从根本上解决了这些问题。通过类型参数化,编译器可以在编译期进行类型检查,将潜在的类型错误提前暴露。例如:
java复制List<String> list = new ArrayList<>();
list.add("Hello");
String str = list.get(0); // 无需强制转换
关键提示:泛型的核心价值在于将类型检查从运行时提前到编译时,这符合Java"尽早发现错误"的设计哲学。
泛型最直接的价值是提供了编译期的类型安全检查。编译器会根据泛型类型参数验证代码的类型正确性,防止在运行时出现ClassCastException。例如:
java复制List<String> strings = new ArrayList<>();
strings.add("text");
Integer num = strings.get(0); // 编译错误
泛型消除了对集合元素进行显式类型转换的需要,使代码更加简洁和安全。根据Oracle官方文档,使用泛型可以减少约30%的类型转换代码。
泛型使得我们可以编写更加通用的代码。例如,一个简单的泛型容器类:
java复制public class Container<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
这个Container类可以用于任何类型,而不需要为每种类型都编写特定的容器类。
泛型类和接口是最常见的泛型应用形式。声明时在类名或接口名后添加类型参数列表,通常使用单个大写字母如T、E、K、V等。
java复制public class GenericClass<T> {
private T data;
public GenericClass(T data) {
this.data = data;
}
public T getData() {
return data;
}
}
泛型可以支持多个类型参数:
java复制public class Pair<K, V> {
private K key;
private V value;
// 构造方法和getter/setter省略
}
在实际项目中,泛型类常用于数据包装、工具类等场景。例如,一个通用的API响应包装类:
java复制public class ApiResponse<T> {
private int code;
private String message;
private T data;
// 构造方法和getter/setter省略
}
泛型方法可以在非泛型类中定义,它允许方法独立于类使用类型参数。
java复制public <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
Java编译器可以自动推断泛型方法的类型参数:
java复制Integer[] intArray = {1, 2, 3};
printArray(intArray); // 编译器推断T为Integer
静态方法也可以使用泛型,但需要注意它不能使用类级别的类型参数:
java复制public class ArrayUtils {
public static <T> T getMiddle(T[] array) {
return array[array.length / 2];
}
}
通过extends关键字可以限制类型参数的上界:
java复制public class NumberContainer<T extends Number> {
private T number;
public double getSquare() {
return number.doubleValue() * number.doubleValue();
}
}
类型参数可以有多个上界,使用&符号连接:
java复制public <T extends Comparable<T> & Serializable> void process(T obj) {
// 方法实现
}
注意事项:类边界必须放在接口边界之前,且只能有一个类边界。
Java的泛型是通过类型擦除实现的,这意味着泛型类型信息在编译后会被擦除。
java复制class Box<T> {} → class Box { Object item; }
java复制class Box<T extends Number> {} → class Box { Number item; }
为了保持多态性,编译器会生成桥方法。例如:
java复制interface Comparable<T> {
int compareTo(T other);
}
class String implements Comparable<String> {
// 编译器会生成桥方法:
public int compareTo(Object other) {
return compareTo((String) other);
}
}
java复制public <T> void createInstance() {
T obj = new T(); // 编译错误
}
解决方法:通过Class对象和反射
java复制public <T> T createInstance(Class<T> clazz) throws Exception {
return clazz.newInstance();
}
java复制T[] array = new T[10]; // 编译错误
解决方法:使用Object数组并强制转换
java复制T[] array = (T[]) new Object[10]; // 会有unchecked cast警告
静态成员不能使用类级别的类型参数:
java复制class Box<T> {
static T defaultValue; // 编译错误
}
java复制void printList(List<?> list) {
for (Object elem : list) {
System.out.println(elem);
}
}
java复制double sumOfList(List<? extends Number> list) {
double sum = 0.0;
for (Number num : list) {
sum += num.doubleValue();
}
return sum;
}
java复制void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) {
list.add(i);
}
}
PECS(Producer Extends, Consumer Super)是使用通配符的重要原则。
当集合作为数据生产者(提供元素)时,使用extends:
java复制public void processElements(List<? extends Number> elements) {
for (Number n : elements) {
System.out.println(n);
}
// elements.add(123); // 编译错误
}
当集合作为数据消费者(接收元素)时,使用super:
java复制public void fillList(List<? super Integer> list) {
for (int i = 0; i < 10; i++) {
list.add(i);
}
// Integer num = list.get(0); // 编译错误
}
java复制public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dest.set(i, src.get(i));
}
}
java复制public static <T> void sort(List<T> list, Comparator<? super T> c) {
// 排序实现
}
虽然运行时类型信息被擦除,但通过反射可以获取部分泛型信息。
java复制public class StringList extends ArrayList<String> {}
Type type = StringList.class.getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
Type[] actualTypeArguments = pt.getActualTypeArguments();
System.out.println(actualTypeArguments[0]); // 输出String
}
java复制public class GenericMethods {
public List<String> getStringList() { return null; }
}
Method method = GenericMethods.class.getMethod("getStringList");
Type returnType = method.getGenericReturnType();
if (returnType instanceof ParameterizedType) {
// 解析类型参数
}
java复制List<Object> objList = new ArrayList<>();
objList.add("string"); // 合法
objList.add(123); // 合法
List<?> wildcardList = new ArrayList<String>();
// wildcardList.add("string"); // 编译错误
wildcardList.add(null); // 唯一允许的操作
java复制void process(List<String> list) {}
void process(List<Integer> list) {} // 编译错误:方法签名冲突
java复制List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
System.out.println(list1.getClass() == list2.getClass()); // 输出true
java复制public interface BaseDao<T, ID> {
T findById(ID id);
List<T> findAll();
T save(T entity);
void delete(T entity);
}
java复制public class EventBus {
private Map<Class<?>, List<Consumer<?>>> handlers = new HashMap<>();
public <T> void registerHandler(Class<T> eventType, Consumer<T> handler) {
handlers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler);
}
@SuppressWarnings("unchecked")
public <T> void publish(T event) {
List<Consumer<?>> consumers = handlers.get(event.getClass());
if (consumers != null) {
consumers.forEach(consumer -> ((Consumer<T>) consumer).accept(event));
}
}
}
java复制List<String> strings = new ArrayList<>();
List rawList = strings; // 合法但危险
rawList.add(123); // 运行时异常
java复制List<String>[] lists = new List<String>[10]; // 编译错误
解决方法:使用通配符类型
java复制List<?>[] lists = new List<?>[10];
不能直接捕获泛型类型的异常:
java复制public static <T extends Exception> void test() {
try {
// ...
} catch (T e) { // 编译错误
// ...
}
}
Java数组是协变的:
java复制Number[] numbers = new Integer[10]; // 合法
numbers[0] = 1.0; // 运行时异常
泛型是不变的:
java复制List<Number> numbers = new ArrayList<Integer>(); // 编译错误
java复制public abstract class Comparable<T extends Comparable<T>> {
public abstract int compareTo(T other);
}
这种模式在实现可比较的类时很有用:
java复制public class MyClass extends Comparable<MyClass> {
@Override
public int compareTo(MyClass other) {
// 实现比较逻辑
}
}
java复制public static <T extends Comparable<T>> T max(List<T> list) {
if (list.isEmpty()) throw new IllegalArgumentException();
T result = list.get(0);
for (T item : list) {
if (item.compareTo(result) > 0) {
result = item;
}
}
return result;
}
Java 10引入的var关键字可以与泛型一起使用:
java复制var list = new ArrayList<String>(); // 推断为ArrayList<String>
Java 7引入的钻石操作符在匿名类中的使用限制在后续版本中得到改进:
java复制List<String> list = new ArrayList<>() { // Java 9+允许
// 匿名类实现
};
Java 16引入的模式匹配instanceof可以与泛型一起使用:
java复制if (obj instanceof List<?> list) {
// 使用list
}
*相当于Java的?java复制public class ApiRequest<T> {
private String requestId;
private T payload;
// 构造方法和getter/setter
}
public class ApiResponse<T> {
private String requestId;
private int status;
private T data;
// 构造方法和getter/setter
}
java复制public interface Handler<I, O> {
O handle(I input);
}
public class ProcessingPipeline<I, O> {
private List<Handler<?, ?>> handlers = new ArrayList<>();
public <T> ProcessingPipeline<I, O> addHandler(Handler<? super T, ? extends O> handler) {
handlers.add(handler);
return this;
}
@SuppressWarnings("unchecked")
public O execute(I input) {
Object result = input;
for (Handler handler : handlers) {
result = handler.handle(result);
}
return (O) result;
}
}
java复制public class GenericBuilder<T> {
private Supplier<T> instantiator;
private List<Consumer<T>> modifiers = new ArrayList<>();
public GenericBuilder(Supplier<T> instantiator) {
this.instantiator = instantiator;
}
public static <T> GenericBuilder<T> of(Supplier<T> instantiator) {
return new GenericBuilder<>(instantiator);
}
public <U> GenericBuilder<T> with(BiConsumer<T, U> consumer, U value) {
modifiers.add(instance -> consumer.accept(instance, value));
return this;
}
public T build() {
T value = instantiator.get();
modifiers.forEach(modifier -> modifier.accept(value));
return value;
}
}
在实际项目中,我发现泛型的正确使用可以显著提高代码的安全性和可维护性。特别是在框架设计和通用工具类开发中,合理运用泛型能够减少重复代码,同时保持类型安全。一个常见的经验是:当你在多个地方看到相似的代码结构,只是处理的类型不同时,就应该考虑使用泛型进行抽象。