一、类型之间的关系三个类如下class Animal{void eat(){}} class Cat extends Animal{void meow(){}} class Dog extends Animal{void bark(){}}1、核心规则泛型不变性ListAnimal和ListDog之间没有父子类型关系哪怕Dog继承Animal。通配符协变/逆变List? extends Animal可以接收ListAnimal、ListDog、ListCat不可接收ListObject。List? super Animal可以接收ListAnimal、ListObject不可接收ListDog、ListCat。2、类型之间的关系2.1、List? extends Animal2.1.1、List? extends Animal和其他 List 的子类型关系List? extends Animal表示“元素类型是 Animal 的某个未知子类含 Animal 本身的 List”。List? extends Animal是ListAnimal、ListDog、ListCat的公共父类型协变。javaList? extends Animal list; list new ArrayListAnimal(); // ✅ list new ArrayListDog(); // ✅ list new ArrayListCat(); // ✅ // 甚至可以 list new ArrayListAnimal() {{ add(new Cat()); }};ListObject不在这个体系中。Object不是Animal的子类所以ListObject不能赋值给List? extends Animal编译错误。总结关系向上转型方向List? extends Animal ↑ ├── ListAnimal ├── ListDog └── ListCat ​ ListObject ← 不相关即ListAnimal、ListDog、ListCat都是List? extends Animal的子类型而ListObject不是。2.1.2、 List? extends Animal 与 Cat、Dog、Animal 本身的关系List? extends Animal是一个容器它可以持有Animal、Dog、Cat的实例但前提是由具体的子类容器在运行时承载。例如ListDog只能存DogListCat只能存Cat。通配符本身不代表可以容纳任何 Animal 子类对象而是声明这个列表可能是存放某种特定子类的。2.2、List? super Animal2.2.1.List? super Animal和其他类型之间的关系List? super Animal表示“元素类型是Animal或其某个父类的列表”。ListAnimal、ListObject、ListDog、ListCat彼此之间没有任何子类型关系它们是不变的只能各自独立使用。List? super Animal是ListAnimal和ListObject的公共父类型通过通配符逆变实现。关系图List? super Animal (顶层逆变通配符) / \ / \ ListAnimal ListObject (两个独立的具体类型) ListDog ListCat (与上面没有任何连线)图中ListAnimal和ListObject都可以向上转型为List? super Animal。但它们之间以及它们与ListDog、ListCat之间均不能相互转型。✅可以赋值给List? super Animal的类型它的子类型ListAnimalListObjectList? super Animal本身自反性❌不能赋值给List? super Animal的类型ListDog——Dog是Animal的子类不是父类ListCat—— 同上List? super Animal是ListAnimal和ListObject的父类型与其他具体参数列表ListDog、ListCat没有子类型关系具体参数列表之间无论类层次如何均无子类型关系泛型不变。2.2.2、 List? super Animal 与 Cat、Dog、Animal 本身的关系List? super Animal是一个容器它可以安全持有Animal、Dog、Cat的实例因为运行时承载它的实际类型一定是Animal或Animal的某个父类如Object这些实际类型都能容纳Animal及其任意子类。通配符本身代表元素类型是某个未知的 Animal 父类这种“下限约束”使得ListAnimal和ListObject都是List? super Animal的子类型而ListDog、ListCat则与其没有父子关系同时它保证了可以向容器中安全写入Animal、Dog、Cat等对象但读取时编译器只能将元素当作最宽泛的Object处理。3、Java类型安全的基石——向上转型子→父永远安全向下转型父→子不安全。对于方法调用方法参数调用者传入的参数把它“当作更宽泛的类型”看所以实参可以比形参更具体子类。内部操作以形参类型为准不会有问题。方法返回值返回的值接收变量类型比返回值更宽泛父类。因为返回的对象至少是返回值类型赋值给父类变量完全安全。4、编译器的“通配符捕获”机制对于List? extends Animal producer4.1. 编译器把? extends Animal转化为一个“未知的捕获类型”当你写List? extends Animal时编译器内部会生成一个捕获类型capture type记为CAP#1它满足CAP#1是Animal的某个未知子类可能为Animal、Dog、Cat等但不知道具体是哪个。于是List? extends Animal实际上被编译器视为ListCAP#1。对于ListE的add方法签名是boolean add(E e);类型捕获后E被替换为CAP#1即boolean add(CAP#1 e);4.2.? super Animal的捕获类型List? super Animal被捕获为ListCAP#2其中CAP#2是Animal的某个父类如Animal或Object。此时add签名是add(CAP#2 e)。5、捕获转换与类型安全的核心逻辑由于Java编译器的捕获类型externds A 被转换为 cap#1,? super A被转换为cap#2。对于List的add方法要求入参类型是形参类型的子类型因为cap#1表示未知的子类型不能确定传入的参数一定是最小子类型所以除了null以外不能传入cap#2表示未知的父类型是可以确定传入参数一定是cap#2的子类型所以可以写入。对于取数据要求接收返回值的变量类型是方法返回值类型的父类型cap#1是未知的子类型所以返回的值类型一定是A的子类型cap#2是未知的父类型返回值不能确定一定是A的子类型所以只能是Object。5.1 对于? extends A协变写入add捕获类型CAP#1是A的未知子类。add的参数类型是CAP#1要传递的实参必须“是CAP#1的子类型”。 由于我们不知道CAP#1究竟是哪一个子类没有任何非 null 对象能保证是它的子类型唯一保证的是null所以除null外禁止写入。✅读取get返回类型是CAP#1要将返回值赋给变量A a需要CAP#1是A的子类型。 这正是捕获约束保证的CAP#1 extends A因此可以安全读为A。✅5.2 对于? super A逆变写入add捕获类型CAP#2是A的未知父类。add参数是CAP#2要传递的实参必须“是CAP#2的子类型”。 已知A是CAP#2的子类A extends CAP#2的逆反而A的任何子类Cat、Dog也是CAP#2的子类传递性。所以可以安全写入A及其子类。✅读取get返回类型是CAP#2要将返回值赋给变量A a需要CAP#2是A的子类。 但捕获约束只保证CAP#2是A的父类无法保证它是A的子类因此不能安全赋给A。唯一能确定的是CAP#2是Object的子类所有引用类型所以只能赋给Object。✅5.3 总结捕获类型与读写规则的完美镜像通配符捕获类型约束add 参数写入可行性get 返回读取可行性? extends ACAP#1 : ACAP#1无对象可保证是其子类 → 仅nullCAP#1可赋给ACAP#1是其子类? super AA : CAP#2CAP#2A及子类是其子类 → 可写CAP#2不能赋给A只能赋给Object