泛型
泛型在编译期提供类型安全检查,消除强制类型转换,提高代码复用性。
泛型类
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擦除规则:
- 无界类型参数
T→Object - 有界类型参数
T extends Foo→Foo - 编译器在必要处自动插入类型转换
由此引发的限制:
// 不能创建泛型数组
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];
}
}