Java Generics WildCard问题:列表<?扩展A>

Sah*_*wal 17 java generics wildcard

假设我有这些课程:车辆,汽车和太空飞船:

class Vehicle{

    void rideVehicle(Vehicle v){
        System.out.println("I am riding a vehicle!");
    }

}

class Car extends Vehicle{
    void rideVehicle(Vehicle c){
        System.out.println("I am riding a car!");
    }
}


class SpaceShip extends Vehicle{
    void rideVehicle(Vehicle c){
        System.out.println("I am riding a spaceship!");
    }

}
Run Code Online (Sandbox Code Playgroud)

我写这个方法addCars:

private static void addCars(List<? extends Vehicle> vcls){
        vcls.add(new Car());
        vcls.add(new Car());
        vcls.add(new Car());

    }
Run Code Online (Sandbox Code Playgroud)

为什么我会得到编译时错误?我知道List是扩展Vehicle的任何X的List的超类型.对?

谢谢

编辑:我得到的错误(编译时):类型List中的方法add(捕获#2-of?extends Vehicle)不适用于参数(Car).

Phi*_* JF 21

方法参数在子类型中是逆变的,并且通过通配符的定义,对于T扩展的每个类型Vehicle Foo<T>都是子类型Foo<* extends Vehicle>.这意味着当你只关心返回类型时,通配符很棒,但是当你想要将类型的值传递给方法时,不能在这种情况下工作.

问题是用户可能试图打电话

List<SpaceShip> l = ...
addCars(l);
Run Code Online (Sandbox Code Playgroud)

如果您的代码是编译的,l那么将是包含3辆汽车的宇宙飞船列表.显然没有好处.


Ami*_*ani 7

是指向您收到编译错误的原因的指针.特别,

List是有界通配符的示例.的?代表一种未知类型,就像我们之前看到的通配符一样.但是,在这种情况下,我们知道这种未知类型实际上是Shape的子类型.(注意:它可能是Shape本身,或者是某些子类;它不需要字面上扩展Shape.)我们说Shape是通配符的上限.

像往常一样,为使用通配符的灵活性付出了代价.这个价格是在方法体中写入形状现在是非法的.例如,这是不允许的:

public void addRectangle(List<? extends Shape> shapes) {
     shapes.add(0, new Rectangle()); // Compile-time error! 
} 
Run Code Online (Sandbox Code Playgroud)

您应该能够弄清楚为什么不允许上面的代码.shapes.add()的第二个参数的类型是?扩展Shape--一个未知的Shape子类型.由于我们不知道它是什么类型,我们不知道它是否是Rectangle的超类型; 它可能是也可能不是这样的超类型,所以在那里传递一个Rectangle是不安全的.


Law*_*Dol 6

提供的List是一些特定类型的Vehicle的列表(其中,为了参数我们将类型称为T),但该特定类型T未知; 它可以是List<Vehicle>,List<Car>等.因此,由于特定通用类型列表的是未知的,这是不允许调用这需要特定的任何方法T作为参数.只有不涉及方法T作为参数可以被调用.

在List的情况下,这样做的实际结果是,这可以防止将任何内容添加到列表中 - 列表不可写.另一方面,可以读取列表,但返回的对象仅称为Vehicle.

也就是说,未知类型T不能提供给List,但是已知的超类Vehicle可以由列表返回.

举个例子,给出你的方法:

private static void addCars(List<? extends Vehicle> vcls) {
Run Code Online (Sandbox Code Playgroud)

你可以想象调用:

List<Car> cars=new ArrayList<Car>();
addCars(cars);
Run Code Online (Sandbox Code Playgroud)

应该允许你直觉.但是,由于addCars只知道列表为"某个子类型Vehicle",因此不允许将对象添加到列表中,因为以下调用将同样有效:

List<Spaceship> ships=new ArrayList<Spaceship>();
addCars(ships);
Run Code Online (Sandbox Code Playgroud)

因此,很明显,在以对象列表为幌子的情况下尝试将Car对象添加到List中一定是错误的Vehicle.