繁体   English   中英

Java通用通配符问题:列表<? extends A>

[英]Java Generics WildCard Question: List<? extends A>

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

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!");
    }

}

我写这个方法addCars:

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

    }

为什么会出现编译时错误? 我知道List是扩展X的所有X的List的超类型。 对?

谢谢

编辑:我得到的错误(编译时):类型List中的方法add(capture#2-of扩展Vehicle)不适用于自变量(Car)。

方法参数在子类型中是互变的,并且根据通配符的定义,对于扩展Vehicle Foo<T>每个类型T都是Foo<* extends Vehicle>的子类型。 这意味着当您只关心返回类型时,通配符非常有用,但是当您想要将类型的值传递给方法时,在这种情况下不起作用。

问题是用户可能会尝试呼叫

List<SpaceShip> l = ...
addCars(l);

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

这是为什么出现编译错误的指针。 特别,

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

与往常一样,使用通配符的灵活性要付出一定的代价。 代价是,现在在方法主体中写入形状是非法的。 例如,这是不允许的:

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

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

提供的List是某些特定类型的Vehicle的列表(在这里,为了便于讨论,我们将类型称为T ),但是该特定类型T未知; 它可能是List<Vehicle>List<Car>等。因此,由于列表的特定泛型类型是未知的,因此不允许调用任何需要将特定T用作参数的方法。 只能调用不包含T作为参数的方法。

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

也就是说,无法将未知类型T提供给列表,但是列表可以返回其已知的Vehicle超类。

通过示例,给出您的方法:

private static void addCars(List<? extends Vehicle> vcls) {

您可以想像地调用:

List<Car> cars=new ArrayList<Car>();
addCars(cars);

应该允许你这样做。 但是,由于addCars仅将列表作为“ Vehicle某些子类型”知道,因此不允许将对象添加到列表,因为以下调用将同样有效:

List<Spaceship> ships=new ArrayList<Spaceship>();
addCars(ships);

由此变得清楚,它必须是错误的做法车的物体所列表假借添加到列表Vehicle对象。

private static void addCars(List<? extends Vehicle> vcls){

应该

private static void addCars(List<? super Vehicle> vcls){

这将修复编译时错误。

编辑: 在这里阅读。

参数的类型是? extends Vehicle ? extends Vehicle ,这意味着一个未知的亚型Vehicle 由于我们不知道它是什么类型,所以我们不知道它是否是Car的超类型; 它可能是也可能不是这种超类型,因此在那儿通过Car并不安全。

阅读本教程的第7页。

当你说<? extends Vehicle> <? extends Vehicle>这意味着它可以是任何类型的扩展车辆。 这意味着有人可以通过List,它将接受它。 现在List<Spaceship>不能将新的Car()作为他的一项。 因此,为避免这些错误,如果使用通配符表达式,则不允许在列表内添加任何对象。

您可以使用:

private static void addCars(List<? super Vehicle> vcls)

(这意味着调用方将传递车辆或超级类型的对象列表)

要么

private static void addCars(List<Vehicle> vcls)

获取和放置原则问题:

你可以试试这个

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

就像是;

List<Integer> ints=Arrays.asList(1,2,3); List<? extends Number> nums=ints; double dbl=sum(nums); // ===ok nums.add(3.14); //===compile-time-error

对于List<Object> ints=Arrays<Object>.asList(1,"two"); List<? super Integer> nums=ints; double dbl=sum(nums); // ===compile-time-error nums.add(3.14); //===ok通配符List<Object> ints=Arrays<Object>.asList(1,"two"); List<? super Integer> nums=ints; double dbl=sum(nums); // ===compile-time-error nums.add(3.14); //===ok List<Object> ints=Arrays<Object>.asList(1,"two"); List<? super Integer> nums=ints; double dbl=sum(nums); // ===compile-time-error nums.add(3.14); //===ok

如果可能的话..

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

那么您可以通过以下方式调用addCars:

List<SpaceShip> ships = new ArrayList<SpaceShip>();
addCars(ships);

使用prince获取并放入通配符如果扩展名使用通配符--->使用get方法如果使用Super的通配符---->使用put方法在这里,您想在List中添加值(意思是put方法)。您可以更改代码

List<? extends Vehicle become List<? super Vehicle> then it will compile legally
private static void addCars(List<? super Vehicle> vcls){
        vcls.add(new Car());
        vcls.add(new Car());
        vcls.add(new Car());
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM