泛型是Java 5引入的重要特性,但很多人只停留在List<String>的层面。为什么不能new T[]?为什么List<String>不能赋值给List<Object>?? extends和? super何时用?本文带你彻底搞懂Java泛型。
一、为什么需要泛型
没有泛型时,集合只能存Object,取出需强转,容易ClassCastException。
java
复制
下载
List list = new ArrayList(); list.add("hello"); list.add(1); String s = (String) list.get(1); // 运行时异常泛型把类型检查提前到编译期:
java
复制
下载
List<String> list = new ArrayList<>(); list.add("hello"); // list.add(1); // 编译报错 String s = list.get(0); // 无需强转二、类型擦除
Java泛型是"伪泛型",编译后类型参数被擦除。
java
复制
下载
List<String> a = new ArrayList<>(); List<Integer> b = new ArrayList<>(); System.out.println(a.getClass() == b.getClass()); // true
擦除规则:
无边界:擦除为Object
T extends Number:擦除为Number编译期插入强制转换
java
复制
下载
// 编译前 List<String> list = new ArrayList<>(); list.add("hello"); String s = list.get(0); // 编译后 List list = new ArrayList(); list.add("hello"); String s = (String) list.get(0);三、类型擦除带来的限制
java
复制
下载
// 1. 不能new泛型数组 // T[] arr = new T[10]; // 编译错误 T[] arr = (T[]) new Object[10]; // 需强转 // 2. 不能用于instanceof // if (obj instanceof List<String>) {} // 编译错误 if (obj instanceof List) {} // 3. 不能定义泛型静态字段 // private static T instance; // 编译错误 // 4. 泛型方法重载冲突 // void method(List<String> l) {} // void method(List<Integer> l) {} // 编译错误,擦除后相同四、通配符
4.1 无界通配符
java
复制
下载
List<?> list = new ArrayList<String>(); // list.add("a"); // 编译错误,只能读 Object o = list.get(0); // 可以读4.2 上界通配符? extends
java
复制
下载
List<? extends Number> list = new ArrayList<Integer>(); Number n = list.get(0); // 可以读 // list.add(1); // 编译错误,不能写
4.3 下界通配符? super
java
复制
下载
List<? super Integer> list = new ArrayList<Number>(); list.add(1); // 可以写 Object o = list.get(0); // 只能读成Object
五、PECS原则
Producer Extends, Consumer Super:
生产者(只读)用
extends消费者(只写)用
super
java
复制
下载
// 生产者:从src读 public static <T> void copy(List<? super T> dest, List<? extends T> src) { for (T t : src) { dest.add(t); } } List<Number> dest = new ArrayList<>(); List<Integer> src = List.of(1, 2, 3); copy(dest, src);六、泛型类与泛型方法
java
复制
下载
// 泛型类 public class Box<T> { private T value; public void set(T value) { this.value = value; } public T get() { return value; } } // 泛型方法 public static <T extends Comparable<T>> T max(T a, T b) { return a.compareTo(b) > 0 ? a : b; } // 多边界 public static <T extends Number & Comparable<T>> void sort(List<T> list) { Collections.sort(list); }七、泛型与继承
java
复制
下载
// 泛型不可变 List<Object> a = new ArrayList<String>(); // 编译错误 // 但数组是协变的 Object[] arr = new String[10]; // 编译通过,运行时可能异常
八、实战:通用Result封装
java
复制
下载
public class Result<T> { private int code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> r = new Result<>(); r.code = 0; r.message = "success"; r.data = data; return r; } public static <T> Result<T> error(int code, String msg) { Result<T> r = new Result<>(); r.code = code; r.message = msg; return r; } } // 使用 Result<User> r = Result.success(new User()); Result<List<User>> list = Result.success(users);九、总结
理解类型擦除是掌握泛型的关键。记住PECS原则:生产者用extends,消费者用super。泛型提升了类型安全,但受擦除限制,不能new数组、不能用instanceof。