Java泛型

发布于:2025-06-27 ⋅ 阅读:(15) ⋅ 点赞:(0)

Java 泛型是 JDK 5 中引入的一项重要特性,它提供了编译时类型安全检测机制,允许在编译时检测到非法的类型。

1. 泛型基础

1.1 泛型的引入

泛型的主要目的是参数化类型,即在类、接口或方法中使用类型参数,使得代码可以处理多种数据类型,同时保持类型安全。

示例:泛型类

public class Box<T> {
    private T content;
    
    public void setContent(T content) {
        this.content = content;
    }
    
    public T getContent() {
        return content;
    }
}
  • T 是类型参数,可以在实例化时指定具体类型(如 IntegerString)。
1.2 泛型类型的实例化

使用泛型类时,需在创建对象时指定具体类型:

Box<String> stringBox = new Box<>();
stringBox.setContent("Hello");
String content = stringBox.getContent(); // 无需强制类型转换
1.3 泛型方法

泛型方法可以独立于类的泛型参数,通过在方法返回类型前声明类型参数:

public static <T> T getFirstElement(T[] array) {
    if (array.length > 0) {
        return array[0];
    }
    return null;
}

调用:

Integer[] numbers = {1, 2, 3};
Integer first = getFirstElement(numbers); // 自动推断 T 为 Integer
1.4 泛型接口

泛型接口的实现类可以是泛型类或具体类型:

public interface List<T> {
    void add(T element);
    T get(int index);
}

// 泛型实现类
public class ArrayList<T> implements List<T> { ... }

// 具体类型实现类
public class StringList implements List<String> { ... }

2. 类型通配符

2.1 无界通配符 (<?>)

用于表示未知类型,通常用于读取元素:

public static void printList(List<?> list) {
    for (Object element : list) {
        System.out.println(element);
    }
}
2.2 上界通配符 (<? extends T>)

限制通配符类型必须是 T 或其子类:

public static double sumOfList(List<? extends Number> list) {
    double sum = 0.0;
    for (Number n : list) {
        sum += n.doubleValue();
    }
    return sum;
}
  • 可读取 Number 类型,但不能添加元素(除 null 外)。
2.3 下界通配符 (<? super T>)

限制通配符类型必须是 T 或其父类:

public static void addNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 10; i++) {
        list.add(i);
    }
}
  • 可添加 Integer 类型元素,但读取时只能作为 Object

3. 类型擦除

Java 泛型是通过类型擦除实现的,即在编译时移除所有泛型类型信息:

  • 泛型类型参数会被替换为其限定类型(若无限定则为 Object)。
  • 示例:Box<T> 擦除后变为 Box<Object>

类型擦除的影响

  • 无法在运行时获取泛型类型信息(如 list.getClass() 返回 ArrayList.class)。
  • 不能创建泛型数组(如 new T[10] 不允许,但可通过 (T[]) new Object[10] 绕过)。

4. 泛型的限制

4.1 不能实例化类型参数
// 错误
public Box() {
    this.content = new T(); // 编译错误
}

解决方法:通过反射或工厂模式。

4.2 静态成员不能使用类的类型参数
public class Box<T> {
    private static T defaultValue; // 错误:静态变量不能引用类型参数
}

但静态泛型方法可以:

public static <T> T getDefault() { ... }
4.3 不能使用基本数据类型

必须使用包装类:

Box<int> intBox = new Box<>(); // 错误
Box<Integer> intBox = new Box<>(); // 正确
4.4 泛型类不能继承 Exception
// 错误
public class MyException<T> extends Exception { ... }

5. 泛型的高级应用

5.1 多重限定

类型参数可同时限定多个接口或类(类必须放在首位):

public class MyClass<T extends Number & Comparable<T>> { ... }
5.2 递归类型限定

用于定义依赖自身类型的泛型:

public interface Comparable<T> {
    int compareTo(T other);
}

public class Person implements Comparable<Person> { ... }
5.3 泛型与反射

虽然类型擦除会移除泛型信息,但可通过反射获取泛型签名:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

public class GenericReflection {
    public static void main(String[] args) {
        List<String> list = List.of("a", "b");
        Type type = list.getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType pType = (ParameterizedType) type;
            Type[] typeArgs = pType.getActualTypeArguments();
            System.out.println(typeArgs[0]); // 输出:class java.lang.String
        }
    }
}

6. 最佳实践

  1. 使用有意义的类型参数名:如 T(Type)、K(Key)、V(Value)、E(Element)。
  2. 优先使用泛型方法:若功能不依赖类的泛型参数,应设计为泛型方法。
  3. 合理使用通配符
    • 只读操作使用 <? extends T>
    • 只写操作使用 <? super T>
    • 读写操作使用具体类型。
  4. 避免原始类型:使用原始类型(如 List 而非 List<String>)会失去泛型的类型安全优势。

总结

Java 泛型通过参数化类型提供了编译时类型安全,避免了运行时的 ClassCastException,并增强了代码的复用性。理解类型擦除、通配符和泛型限制是掌握泛型的关键。在实际开发中,泛型广泛应用于集合框架、反射 API 和设计模式中。


网站公告

今日签到

点亮在社区的每一天
去签到