Java泛型含extends的代码为何可编译?求底层原理
Java泛型extends与通配符<?>的核心逻辑解析
先看你给出的测试代码:
public class Tests { // Some straightforward sample tyes class Vehicle {} class Car extends Vehicle {} // Generic list factory method for Vehicles and all subtypes private static <T extends Vehicle> List<T> createVehicleList () { return new ArrayList<T>(); } @Test public void funWithExtends () { // This works because the type is explicitly specified List<Car> alsoCars = Tests.<Car>createVehicleList(); // This works because of type inference ... I think List<Car> cars = createVehicleList(); // Here's where it gets weird. Why is this legal? Object weirdCars = createVehicleList(); List<?> list = (List<?>)weirdCars; list.add(null); // This next line doesn't compile - I can't add() anything but null // list.add(new Vehicle()); Object el = list.get(0); System.out.println(String.format("el = %s", el)); assertEquals(null, el); } }
一、未指定泛型参数时的类型推断
当你直接调用createVehicleList()且不指定<T>、也不赋值给明确泛型类型的变量时,编译器会根据方法的泛型约束T extends Vehicle,把T推断为约束的上限类型Vehicle,所以此时返回的是List<Vehicle>实例。而因为赋值给了Object,Java的泛型擦除机制会在编译后抹掉泛型信息,所以Object引用的就是一个普通的ArrayList对象,这一步完全合法。
二、List<?>的类型安全限制
把Object强转为List<?>后,这个通配符表示元素类型未知的列表:
- 允许添加
null:因为null是所有引用类型的默认值,无论列表实际存什么类型,null都能兼容,不会破坏类型安全。 - 禁止添加其他对象:比如
new Vehicle(),编译器无法确定这个列表实际是List<Vehicle>还是List<Car>,如果是后者,添加Vehicle会导致类型错误,所以直接编译报错。 - 读取元素只能用
Object接收:因为编译器不知道元素的具体类型,只能用最顶层的引用类型Object来接收。
三、这是泛型底层原理,不是兼容旧版本的特殊场景
这种行为完全基于Java泛型的两个核心设计:
- 泛型擦除机制:Java泛型仅在编译期做类型校验,运行时所有泛型信息都会被擦除,所以
List<Vehicle>和List<Car>在运行时都是List类型,赋值给Object完全合法。 - 通配符的类型安全设计:
List<?>的存在就是为了表示“任意类型的列表”,但为了避免错误的写入操作,编译器限制了仅能添加null,这是泛型类型系统的核心规则,和旧版本兼容无关。
内容的提问来源于stack exchange,提问作者Bean Taxi
相关产品推荐
相关产品推荐

