Java泛型疑问:非泛型Flight类无法添加BaseOffer,泛型Hotel类却可行?
咱们一步一步拆解这个问题,核心是Java泛型的不变性和原始类型的行为在起作用:
1. 非泛型Flight类的报错原因
先看你的Flight类定义:
public class Flight { public List<FlightOffer> flightOffers; }
这里flightOffers是明确的List<FlightOffer>类型。Java里的泛型是不变的——也就是说,List<BaseOffer>不是List<FlightOffer>的父类,反过来也不行。FlightOffer继承自BaseOffer,但这只意味着单个FlightOffer可以赋值给BaseOffer变量,不代表它们的泛型集合可以互相兼容。
所以当你执行flightObject.flightOffers.add(new BaseOffer())时,编译器会严格检查:你试图把一个BaseOffer(父类)添加到只能容纳FlightOffer(子类)的列表里,类型不匹配,自然报错。
2. Hotel类能正常运行的真实原因
你的Hotel类是泛型类,但注意你声明实例的方式:
Hotel hotelObject = new Hotel<String>(); // 左边是原始类型Hotel,不是Hotel<String>
这里你用了原始类型(Raw Type)——也就是不带泛型参数的Hotel。当使用原始类型时,Java编译器会完全禁用泛型类型检查,所有泛型相关的类型信息都会被擦除。
这时候hotelOffers的实际类型会被视为原始的List(没有泛型约束),而不是List<HotelOffer>。原始List可以接受任何Object类型的实例,所以添加BaseOffer当然不会报错。
如果你把声明改成带泛型参数的形式:
Hotel<String> hotelObject = new Hotel<>();
再执行hotelObject.hotelOffers.add(new BaseOffer()),编译器会立刻抛出和Flight类一样的错误——因为此时hotelOffers是List<HotelOffer>,同样不能接受父类BaseOffer。
3. 改成泛型Flight后可以正常添加的原因
当你把Flight改成泛型类:
public class Flight<T> { public List<FlightOffer> flightOffers; }
如果你实例化时用了原始类型:
Flight flightObject = new Flight<>(); // 原始类型Flight
和Hotel的情况一样,编译器会禁用泛型检查,flightOffers被视为原始List,所以可以添加BaseOffer。
但如果你用具体的泛型参数实例化:
Flight<String> flightObject = new Flight<>();
此时flightOffers依然是List<FlightOffer>,添加BaseOffer还是会报错——这才是符合泛型规则的正常行为。
总结一下:
- 非泛型类的泛型成员会被严格检查类型
- 使用原始类型会绕过泛型类型检查,导致"看似可以运行"的情况
- 泛型类改成原始类型实例化时,同样会绕过检查,这就是你看到的"改成泛型就正常"的本质
内容的提问来源于stack exchange,提问作者Emre Akcan

