泛型(Generics)是Java中用于增强类型安全、减少代码冗余和提升灵活性的核心特性。以下从多个维度详细解析泛型的应用场景,涵盖基础到高级用法,并辅以具体示例。
1. 集合框架中的泛型
泛型在集合中最为常见,用于明确集合元素的类型,避免类型转换错误。
示例:
// 泛型List:存储String类型元素
List<String> names = new ArrayList<>();
names.add("Alice");
// names.add(123); // 编译错误,类型不匹配
String name = names.get(0); // 无需强制转换
// 泛型Map:键为Integer,值为String
Map<Integer, String> idToName = new HashMap<>();
idToName.put(1, "Alice");
String value = idToName.get(1); // 直接返回String
优势:
类型安全:编译器检查元素类型,避免运行时ClassCastException。
代码简洁:无需手动类型转换。
2. 自定义泛型类
通过泛型参数定义类,使其支持多种数据类型。
示例:通用容器类
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(); // 直接返回String
Box<Integer> intBox = new Box<>();
intBox.setContent(100);
int num = intBox.getContent(); // 自动拆箱为int
应用场景:
数据模型(如Response)、工具类(如缓存容器)。
3. 泛型方法
在方法中定义独立于类的泛型参数,实现类型安全的通用逻辑。
示例:交换数组元素
public class ArrayUtils {
public static <T> void swap(T[] array, int i, int j) {
T temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
// 使用示例
String[] names = {"Alice", "Bob"};
ArrayUtils.swap(names, 0, 1); // 自动推断T为String
System.out.println(Arrays.toString(names)); // [Bob, Alice]
优势:
独立于类:泛型参数仅作用于方法。
灵活性:同一方法处理不同类型数据。
4. 泛型接口
定义接口时使用泛型参数,支持多态实现。
示例:数据处理器接口
public interface Processor<T> {
void process(T data);
}
// 实现类:处理字符串
public class StringProcessor implements Processor<String> {
@Override
public void process(String data) {
System.out.println("Processing: " + data.toUpperCase());
}
}
// 使用示例
Processor<String> processor = new StringProcessor();
processor.process("hello"); // 输出:Processing: HELLO
应用场景:
回调机制、策略模式、事件监听。
5. 通配符(Wildcards)
使用?实现灵活的类型约束,分为上界(<? extends T>)和下界(<? super T>)。
示例1:上界通配符(读取数据)
public static double sum(List<? extends Number> list) {
return list.stream().mapToDouble(Number::doubleValue).sum();
}
// 使用示例
List<Integer> integers = List.of(1, 2, 3);
List<Double> doubles = List.of(1.5, 2.5);
sum(integers); // 6.0
sum(doubles); // 4.0
示例2:下界通配符(写入数据)
public static void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
}
// 使用示例
List<Number> numbers = new ArrayList<>();
addNumbers(numbers); // 合法
System.out.println(numbers); // [1, 2]
PECS原则:
Producer-Extends:当需要从集合中读取数据时,使用<? extends T>。
Consumer-Super:当需要向集合中写入数据时,使用<? super T>。
6. 泛型与继承
泛型类型参数支持继承,但需注意类型擦除的影响。
示例:泛型类继承
class Animal {}
class Dog extends Animal {}
public class AnimalBox<T extends Animal> {
private T animal;
public void setAnimal(T animal) {
this.animal = animal;
}
public T getAnimal() {
return animal;
}
}
// 使用示例
AnimalBox<Dog> dogBox = new AnimalBox<>();
dogBox.setAnimal(new Dog());
Animal animal = dogBox.getAnimal(); // 多态赋值
注意:
类型擦除:AnimalBox和AnimalBox在运行时均为AnimalBox。
7. 类型擦除与反射
泛型在编译时擦除类型信息,但可通过反射绕过限制(需谨慎)。
示例:反射操作泛型字段
public class GenericClass<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
// 反射赋值(绕过类型检查)
GenericClass<String> stringBox = new GenericClass<>();
Field valueField = GenericClass.class.getDeclaredField("value");
valueField.setAccessible(true);
valueField.set(stringBox, 100); // 运行时抛出ClassCastException
风险:
类型不安全:可能引发运行时异常。
8. 泛型与静态成员
静态成员无法使用类的泛型参数,但可定义静态泛型方法。
示例:静态泛型方法
public class Utils {
public static <T> T getDefaultValue(Class<T> clazz) {
if (clazz == String.class) {
return clazz.cast("default");
}
return null;
}
}
// 使用示例
String defaultValue = Utils.getDefaultValue(String.class);
限制:
静态方法无法访问类的泛型参数。
9. 泛型数组的限制与替代
Java不允许直接创建泛型数组,但可通过通配符或反射实现。
示例1:通配符数组(不推荐)
List<?>[] lists = new ArrayList<?>[10];
lists[0] = new ArrayList<String>();
lists[1] = new ArrayList<Integer>();
示例2:反射创建泛型数组
public static <T> T[] createArray(Class<T> clazz, int size) {
return (T[]) Array.newInstance(clazz, size);
}
// 使用示例
String[] strings = createArray(String.class, 5);
替代方案:
优先使用集合(如List)代替数组。
10. 框架中的泛型应用
泛型在主流框架中广泛应用,提升类型安全。
示例:Spring Data JPA
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 泛型参数:实体类型为User,ID类型为Long
}
// 使用示例
User user = userRepository.findById(1L).orElse(null);
优势:
强类型API:避免类型转换,减少错误。
11. 泛型在函数式编程中的应用
结合Java 8+的Stream API和Lambda表达式,泛型增强数据处理能力。
示例:通用过滤方法
public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
return list.stream().filter(predicate).collect(Collectors.toList());
}
// 使用示例
List<Integer> numbers = List.of(1, 2, 3, 4);
List<Integer> evenNumbers = filter(numbers, n -> n % 2 == 0);
优势:
代码复用:同一方法处理不同类型的数据集合。
12. 泛型与异常处理
泛型类或方法无法直接抛出泛型类型的异常,但可通过包装类实现。
示例:泛型结果包装
public class Result<T> {
private T data;
private Exception error;
public Result(T data) {
this.data = data;
}
public Result(Exception error) {
this.error = error;
}
}
// 使用示例
Result<Integer> result = someOperation();
if (result.error != null) {
// 处理异常
}
13. 泛型在反射中的应用
通过反射获取泛型类型信息(需处理类型擦除)。
示例:获取泛型父类类型
public class StringList extends ArrayList<String> {}
// 反射获取泛型参数
Type genericSuperclass = StringList.class.getGenericSuperclass();
if (genericSuperclass instanceof ParameterizedType) {
Type[] typeArgs = ((ParameterizedType) genericSuperclass).getActualTypeArguments();
System.out.println(typeArgs[0]); // 输出:class java.lang.String
}
限制:
类型擦除导致部分信息丢失。
14. 泛型与枚举
枚举类不支持泛型参数,但可通过嵌套类模拟。
示例:泛型枚举模拟
public enum Operation {
ADD((a, b) -> a + b),
SUBTRACT((a, b) -> a - b);
private final BiFunction<Integer, Integer, Integer> action;
Operation(BiFunction<Integer, Integer, Integer> action) {
this.action = action;
}
public int apply(int a, int b) {
return action.apply(a, b);
}
}
// 使用示例
int result = Operation.ADD.apply(5, 3); // 8
15. 泛型在嵌套类中的应用
嵌套类可使用外部类的泛型参数。
示例:链表节点
public class LinkedList<T> {
private Node<T> head;
private static class Node<T> {
T data;
Node<T> next;
Node(T data) {
this.data = data;
}
}
}
16. 泛型与多态
通过泛型实现更灵活的多态设计。
示例:多态处理器
public interface Handler<T> {
void handle(T data);
}
public class StringHandler implements Handler<String> {
@Override
public void handle(String data) {
System.out.println("Handling string: " + data);
}
}
public class NumberHandler implements Handler<Number> {
@Override
public void handle(Number data) {
System.out.println("Handling number: " + data);
}
}
17. 泛型边界(多重边界)
限制类型参数必须实现多个接口或继承特定类。
示例:多重边界
public class Utility<T extends Comparable<T> & Serializable> {
public boolean isEqual(T a, T b) {
return a.compareTo(b) == 0;
}
}
18. 泛型与Lambda表达式
结合泛型与Lambda实现灵活的行为传递。
示例:通用比较器
public static <T> Comparator<T> createComparator(Function<T, Comparable> keyExtractor) {
return (a, b) -> keyExtractor.apply(a).compareTo(keyExtractor.apply(b));
}
// 使用示例
List<Person> people = ...;
people.sort(createComparator(Person::getAge));
19. 泛型在注解中的应用
注解不支持泛型参数,但可通过参数化实现类似效果。
示例:泛型注解模拟
public @interface Validate {
Class<? extends Validator<?>> validator();
}
public interface Validator<T> {
boolean validate(T obj);
}
20. 泛型在测试中的应用(如JUnit)
泛型提升测试代码的复用性。
示例:通用测试工具类
public class TestUtils {
public static <T> void assertListEquals(List<T> expected, List<T> actual) {
assertEquals(expected.size(), actual.size());
for (int i = 0; i < expected.size(); i++) {
assertEquals(expected.get(i), actual.get(i));
}
}
}
最佳实践:
优先使用泛型集合:避免使用原生类型如List。
合理使用通配符:在API设计中平衡灵活性与安全性。
避免反射操作泛型:防止运行时类型错误。
明确泛型边界:通过限制类型参数。
类型安全的转换:利用Class参数进行安全实例化。
通过全面应用泛型,可显著提升代码的可维护性、安全性和灵活性,适应复杂多变的业务需求。