Java 泛型 (Generics)
泛型是 Java 5 引入的重要特性,它允许在定义类、接口和方法时使用类型参数(type parameters),从而使代码可以适用于多种类型,同时提供编译时类型安全检查。
泛型的基本概念
- 泛型类
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("Hello");
String value = stringBox.getContent(); // 无需类型转换
- 泛型接口
java
public interface Pair<K, V> {
K getKey();
V getValue();
} - 泛型方法
java
public class Util {
public static T getMiddle(T[] array) {
return array[array.length / 2];
}
}
// 使用
String[] names = {“Alice”, “Bob”, “Charlie”};
String middle = Util.getMiddle(names); // 类型推断可省略
泛型的类型参数命名约定
E - 元素 (Element),用于集合框架
K - 键 (Key)
V - 值 (Value)
N - 数字 (Number)
T - 类型 (Type)
S, U, V 等 - 第二、第三、第四类型
类型通配符
- 无界通配符 (?)
java
public void printList(List<?> list) {
for (Object elem : list) {
System.out.println(elem);
}
} - 上界通配符 (? extends Type)
java
public double sumOfList(List<? extends Number> list) {
double sum = 0.0;
for (Number num : list) {
sum += num.doubleValue();
}
return sum;
} - 下界通配符 (? super Type)
java
public void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 5; i++) {
list.add(i);
}
}
类型擦除
Java 泛型是通过类型擦除实现的,这意味着泛型信息只在编译时存在,运行时会被擦除:
泛型类型参数会被替换为它们的边界或 Object
必要时插入类型转换
桥方法被生成以保持多态
java
// 编译前
List list = new ArrayList<>();
// 编译后(类型擦除)
List list = new ArrayList();
泛型的限制
不能使用基本类型作为类型参数:
java
List // 错误,必须使用 List
不能创建泛型数组:
java
T[] array = new T[10]; // 错误
不能实例化类型参数:
java
public T create() {
return new T(); // 错误
}
不能在静态上下文中使用类的类型参数:
java
public class Foo {
private static T value; // 错误
}
不能抛出或捕获泛型类的实例:
java
try {
// …
} catch (T ex) { // 错误
// …
}
泛型与继承
泛型类即使类型参数有继承关系,它们之间也没有继承关系:
java
List 不是 List 的子类
通配符与继承
java
List<? extends Number> 可以接受 List 或 List
List<? super Integer> 可以接受 List 或 List 或 List
PECS 原则 (Producer-Extends, Consumer-Super)
Producer (生产者,提供数据): 使用 extends
Consumer (消费者,接收数据): 使用 super
java
// 生产者示例
public static void copy(List<? extends T> source, List<? super T> destination) {
for (T item : source) {
destination.add(item);
}
}
范型方法特别说明:
在 Java 泛型方法中,如果参数是泛型类型(如 T),而该方法需要处理具体类型(如 String 和 Integer)的特定逻辑,可以通过以下几种方式实现:
方法 1:使用 instanceof 进行类型检查
在泛型方法内部,可以通过 instanceof 检查泛型参数的具体类型,然后执行不同的逻辑。
示例代码
java
public class GenericMethodExample {
// 泛型方法
public static <T> void process(T input) {if (input instanceof String) {// 处理 String 类型String str = (String) input;System.out.println("String: " + str.toUpperCase());} else if (input instanceof Integer) {// 处理 Integer 类型Integer num = (Integer) input;System.out.println("Integer: " + (num * 2));} else {// 其他类型System.out.println("Unknown type: " + input);}
}public static void main(String[] args) {process("hello"); // 输出: String: HELLOprocess(123); // 输出: Integer: 246process(3.14); // 输出: Unknown type: 3.14
}
}
说明
instanceof 检查泛型参数的具体类型。
需要手动强制类型转换((String) input)。
适用于少量已知类型的情况,但如果类型很多,代码会变得冗长。
方法 2:方法重载(推荐)
如果逻辑差异较大,可以直接定义多个重载方法,而不是使用泛型方法。
示例代码
java
public class OverloadExample {
// 处理 String
public static void process(String input) {System.out.println("String: " + input.toUpperCase());
}// 处理 Integer
public static void process(Integer input) {System.out.println("Integer: " + (input * 2));
}// 处理其他类型
public static <T> void process(T input) {System.out.println("Unknown type: " + input);
}public static void main(String[] args) {process("hello"); // 调用 String 版本process(123); // 调用 Integer 版本process(3.14); // 调用泛型版本
}
}
说明
更符合 Java 的类型安全机制。
避免了 instanceof 和强制类型转换。
代码更清晰,推荐使用。
方法 3:使用 Class 参数(反射)
如果需要动态处理类型,可以传入 Class 参数,结合反射进行操作。
示例代码
java
public class ClassParamExample {
public static <T> void process(T input, Class<T> type) {if (type == String.class) {String str = (String) input;System.out.println("String: " + str.toUpperCase());} else if (type == Integer.class) {Integer num = (Integer) input;System.out.println("Integer: " + (num * 2));} else {System.out.println("Unknown type: " + input);}
}public static void main(String[] args) {process("hello", String.class); // 输出: String: HELLOprocess(123, Integer.class); // 输出: Integer: 246process(3.14, Double.class); // 输出: Unknown type: 3.14
}
}
说明
适用于需要动态类型检查的场景。
仍然需要强制类型转换,但避免了 instanceof 的冗余检查。
方法 4:使用策略模式(面向对象)
如果逻辑复杂,可以使用策略模式,将不同类型的处理逻辑封装成不同的类。
示例代码
java
interface Processor {
void process(T input);
}
class StringProcessor implements Processor {
@Override
public void process(String input) {
System.out.println("String: " + input.toUpperCase());
}
}
class IntegerProcessor implements Processor {
@Override
public void process(Integer input) {
System.out.println("Integer: " + (input * 2));
}
}
public class StrategyPatternExample {
public static <T> void process(T input, Processor<? super T> processor) {processor.process(input);
}public static void main(String[] args) {process("hello", new StringProcessor()); // 输出: String: HELLOprocess(123, new IntegerProcessor()); // 输出: Integer: 246
}
}
说明
适用于复杂逻辑,符合开闭原则(OCP)。
扩展性强,但代码量较多。
推荐方案
如果类型较少且明确 → 方法重载(最简洁、最安全)。
如果需要动态处理 → Class 参数 或 策略模式。
避免滥用 instanceof,除非逻辑非常简单。
希望这些方法能帮助你灵活处理泛型参数! 🚀
总结
Java 泛型提供了编译时类型安全,减少了运行时类型转换错误,使代码更清晰、更安全。理解类型擦除、通配符和 PECS 原则对于有效使用泛型至关重要。