对 Java 中逆变的误解及代码示例

Misunderstanding on Contravariance in Java with code example

我正在尝试一个关于 Java 中逆变的易于理解的示例,但理解有问题。 在下面的示例中,我有 List<? super CarBill> list1 。我的理解是我应该能够添加 CarBill 的任何 superclass 的对象。按照这个逻辑,我应该能够向它添加 Bill class 的对象,对吗? 我收到编译错误。

package Generics;

import java.util.ArrayList;
import java.util.List;

public class VarianceTests {
    static class Bill{
        String vName;
        String type;
        Bill(String vName){
            this.vName=vName;
        }
        Bill(String vName,String type){
            this.vName=vName;
            this.type=type;
        }
    }

    static class CarBill extends Bill{
        String name;
        CarBill(String name)
        {
            super(name,"Car");
        }
    }

    static class Car<T extends Bill> {
        T car;
        Car(T car){
            this.car=car;
        }

        String getNameOfCar() {
            return car.vName;
        }
    }


public static void main(String args[]) {
    CarBill cBill = new CarBill("Baleno");
    Bill bill=new Bill("Whatever");
    Car car = new Car(bill); //cBill is valid too as Car accepts <? extends Bill>

    List<? super CarBill> list1 = new ArrayList<>();
    list1.add(cBill);
    list1.add(bill);
}

public void acceptListOfCars(List<? extends Bill> list1) {
    Bill b = list1.get(0); //Valid syntax

}

}

My understanding is i should be able to add an object of any superclass of CarBill

没有

A List<? super CarBill> 不是一个可以接受 CarBill 的任何超类型对象的列表。这是一个列表,它将接受 CarBill 的某些 特定 超类型的对象,但它是哪个超类型是未知的。

您可以添加类型 CarBill 的任何对象,因为它保证是类型 ? 的子类型。但是 CarBill 的超类型 而不是 保证是 ? 的子类型。

例如:

List<? super CarBill> myList = new ArrayList<Bill>();

Object o = "Anything";

ObjectCarBill 的超类型。因此,如果您可以将 CarBill 的任何超类型添加到列表中,您就可以将 o 添加到列表中,这意味着您可以添加 任何到列表。

不完全是。

让我们从这段代码开始:

List<Integer> listOfInts = new ArrayList<Integer>();
List<Number> listOfNumbers = listOfInts;
listOfNumbers.add(5.5D); // a double
int i = listOfInts.get(0); // uhoh!

以上代码实际上无法编译;第二行是一个无效的赋值。你的思路会说:但是.. 为什么? Number 是 Integer 的超类型,因此,整数列表通常也是数字列表,不是吗?但是第三行说明了为什么这条推理线是不正确的。 Java 不会让你写上面的代码。你可以写的是:同样的事情,但这次我们调整第二行:

List<Integer> listOfInts = new ArrayList<Integer>();
List<? extends Number> listOfNumbers = listOfInts;
listOfNumbers.add(5.5D); // a double
int i = listOfInts.get(0); // uhoh!

这一次,您在第三行收到编译器错误:您不能向该列表中添加双精度数。但是,如果你从中读取,你会得到数字(不是对象)。这一切都很好:无论我们尝试什么,上面的代码片段都永远不会编译,因为它试图将双精度值添加到整数列表中。

重点是:List<? extends Number>不代表:"This list contains numbers, or any subtypes thereof"。不;就像 List x = new ArrayList() 是合法的 java 一样,List<Number> 表示 'this list contains numbers or any subtypes thereof' 因为数字的任何子类型的任何实例本身都可以用作数字。 List<? extends Number> 表示:这是一个列表,仅限于包含某些特定类型的实例,但不知道是哪种类型。已知的是,无论该类型是什么,它要么是 Number 要么是它的某个子类型。

因此,您不能将 任何东西 添加到 List<? extends Number>

对于超级,类似的故事:

List<? super CarBill> 表示:这是一个列表,仅限于包含某些特定类型的实例,但不知道是哪种类型。众所周知,无论它是什么类型,它要么是 CarBill 要么是其中的某种 SUPERtype。

这样做的好处是,您可以将 CarBill 个实例添加到 List<? super CarBill> 变量。当你从中读取时,你会得到对象。

你的理解有误。

List<? super CarBill>表示该列表可以是CarBill的任何超class或CarBill本身的列表。可以是List<Object>,也可以是List<Bill>,甚至可以是List<CarBill>。它实际上是哪一个?我们不知道。

因此,您不能将 Bill 添加到 List<? super CarBill>,因为如果列表实际上是 List<CarBill> 怎么办?您不能将 Bill 添加到 List<CarBill>

换句话说,您只能将 CarBillCarBill 或子 class 添加到 List<? super CarBill>.

如果您打算创建一个可以存储任何类型 Bill 的列表,您可以创建一个 List<Bill>.

This post 也可能有帮助。