泛型

泛型在编译期提供类型安全检查,消除强制类型转换,提高代码复用性。

泛型类

public class Box<T> {
    private T value;
 
    public Box(T value) { this.value = value; }
    public T get() { return value; }
}
 
Box<String> strBox = new Box<>("hello");
Box<Integer> intBox = new Box<>(42);

泛型方法

public class Utils {
    // 方法级别的类型参数,独立于类的泛型
    public static <T> List<T> repeat(T item, int times) {
        List<T> list = new ArrayList<>();
        for (int i = 0; i < times; i++) list.add(item);
        return list;
    }
 
    // 多个类型参数
    public static <K, V> Map<K, V> mapOf(K key, V value) {
        Map<K, V> map = new HashMap<>();
        map.put(key, value);
        return map;
    }
}
 
List<String> list = Utils.repeat("hello", 3);

泛型接口

public interface Transformer<I, O> {
    O transform(I input);
}
 
// 实现时指定具体类型
public class StringToInt implements Transformer<String, Integer> {
    @Override
    public Integer transform(String input) {
        return Integer.parseInt(input);
    }
}
 
// 实现时保留泛型
public class Identity<T> implements Transformer<T, T> {
    @Override
    public T transform(T input) { return input; }
}

类型边界

上界(extends)

// T 必须是 Number 或其子类
public <T extends Number> double sum(List<T> list) {
    return list.stream().mapToDouble(Number::doubleValue).sum();
}
 
// 多重边界:只能有一个类,接口可以多个
public <T extends Comparable<T> & Cloneable> T max(T a, T b) {
    return a.compareTo(b) >= 0 ? a : b;
}

通配符

// ? extends T:上界通配符,只读(生产者)
public double sumList(List<? extends Number> list) {
    return list.stream().mapToDouble(Number::doubleValue).sum();
}
 
// ? super T:下界通配符,只写(消费者)
public void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
}
 
// ?:无界通配符,等价于 ? extends Object
public void printList(List<?> list) {
    list.forEach(System.out::println);
}

PECS 原则(Producer Extends, Consumer Super):

  • 从集合中读取数据 → ? extends T
  • 向集合中写入数据 → ? super T
  • 既读又写 → 具体类型参数 T
// Collections.copy 的设计体现了 PECS
public static <T> void copy(List<? super T> dest, List<? extends T> src)

类型擦除

Java 泛型在编译后类型参数被擦除,运行时不保留泛型信息。

List<String> strings = new ArrayList<>();
List<Integer> ints = new ArrayList<>();
 
// 运行时类型相同
System.out.println(strings.getClass() == ints.getClass()); // true
System.out.println(strings.getClass()); // class java.util.ArrayList

擦除规则

  • 无界类型参数 TObject
  • 有界类型参数 T extends FooFoo
  • 编译器在必要处自动插入类型转换

由此引发的限制

// 不能创建泛型数组
T[] arr = new T[10];       // 编译错误
 
// 不能用 instanceof 检查参数化类型
if (obj instanceof List<String>) {} // 编译错误
if (obj instanceof List<?>) {}      // 正确
 
// 不能直接实例化类型参数
T t = new T();             // 编译错误
 
// 静态成员不能使用类的类型参数
static T instance;         // 编译错误

泛型与继承

// List<Integer> 不是 List<Number> 的子类型(即使 Integer 是 Number 子类)
List<Integer> ints = new ArrayList<>();
List<Number> nums = ints; // 编译错误!
 
// 用通配符建立类型关系
List<? extends Number> nums2 = ints; // 正确,但只读

获取泛型类型信息

运行时获取泛型参数(通过匿名子类绑定类型):

// 利用 TypeToken 模式(Guava / Jackson 都使用此技术)
Type type = new TypeReference<List<String>>() {}.getType();
 
// 在抽象类中获取子类传入的类型参数
public abstract class BaseDao<T> {
    private final Class<T> entityClass;
 
    @SuppressWarnings("unchecked")
    public BaseDao() {
        Type superClass = getClass().getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType) superClass;
        this.entityClass = (Class<T>) pt.getActualTypeArguments()[0];
    }
}

相关链接