简体   繁体   English

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

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

Let's say I have these classes : Vehicle, Car and Spaceship: 假设我有以下课程:车辆,汽车和太空飞船:

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

}

and I write this method addCars: 我写这个方法addCars:

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

    }

Why do I get a compile time error?? 为什么会出现编译时错误? I understand that List is a supertype of List for any X that extends Vehicle. 我知道List是扩展X的所有X的List的超类型。 right? 对?

Thanks 谢谢

Edit: the error I get (compile-time) : The method add(capture#2-of ? extends Vehicle) in the type List is not applicable for the arguments (Car). 编辑:我得到的错误(编译时):类型List中的方法add(capture#2-of扩展Vehicle)不适用于自变量(Car)。

Method arguments are contravariant in the subtype, and by the definition of the wildcard, for every type T that extends Vehicle Foo<T> is a subtype of Foo<* extends Vehicle> . 方法参数在子类型中是互变的,并且根据通配符的定义,对于扩展Vehicle Foo<T>每个类型T都是Foo<* extends Vehicle>的子类型。 The implication of this is that wildcards are great when you only care about the return type, but dont work in situations like this when you want to pass a value of the type to a method. 这意味着当您只关心返回类型时,通配符非常有用,但是当您想要将类型的值传递给方法时,在这种情况下不起作用。

The problem is that a user might try to call 问题是用户可能会尝试呼叫

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

if your code were to compile, l would then be a list of spaceships containing 3 cars. 如果要编译您的代码,则l将是包含3辆汽车的宇宙飞船的列表。 Clearly no good. 显然没有好处。

Here's a pointer to why you're getting a compile error. 这是为什么出现编译错误的指针。 Specifically, 特别,

List is an example of a bounded wildcard. 列表是有界通配符的示例。 The ? stands for an unknown type, just like the wildcards we saw earlier. 代表未知类型,就像我们之前看到的通配符一样。 However, in this case, we know that this unknown type is in fact a subtype of Shape. 但是,在这种情况下,我们知道该未知类型实际上是Shape的子类型。 (Note: It could be Shape itself, or some subclass; it need not literally extend Shape.) We say that Shape is the upper bound of the wildcard. (注意:它可以是Shape本身,也可以是某些子类;它不需要扩展Shape。)我们说Shape是通配符的上限。

There is, as usual, a price to be paid for the flexibility of using wildcards. 与往常一样,使用通配符的灵活性要付出一定的代价。 That price is that it is now illegal to write into shapes in the body of the method. 代价是,现在在方法主体中写入形状是非法的。 For instance, this is not allowed: 例如,这是不允许的:

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

You should be able to figure out why the code above is disallowed. 您应该能够弄清楚为什么不允许使用上面的代码。 The type of the second parameter to shapes.add() is ? shapes.add()的第二个参数的类型是? extends Shape-- an unknown subtype of Shape. 扩展Shape-Shape的未知子类型。 Since we don't know what type it is, we don't know if it is a supertype of Rectangle; 由于我们不知道它是什么类型,所以我们不知道它是否是Rectangle的超类型。 it might or might not be such a supertype, so it isn't safe to pass a Rectangle there. 它可能是也可能不是这种超类型,因此在此处传递Rectangle是不安全的。

The supplied List is a list of some specific type of Vehicle (where, for the sake of argument we will refer to the type as T ), but that specific type T is not known; 提供的List是某些特定类型的Vehicle的列表(在这里,为了便于讨论,我们将类型称为T ),但是该特定类型T未知; it may be List<Vehicle> , List<Car> , etc. Therefore, since the specific generic type of the list is unknown, it is not permitted to invoke any method which requires the specific T as an argument. 它可能是List<Vehicle>List<Car>等。因此,由于列表的特定泛型类型是未知的,因此不允许调用任何需要将特定T用作参数的方法。 Only methods which don't involve T as an argument can be invoked. 只能调用不包含T作为参数的方法。

The practical upshot of this, in the case of List, is that this prevents anything being added to the list - the list is not writable. 在使用List的情况下,这样做的实际结果是可以防止将任何内容添加到列表中-列表不可写。 On the other hand, the list can be read, but with the returned objects only known as Vehicle . 另一方面, 可以读取列表,但返回的对象仅称为Vehicle

That is, the unknown type T cannot be supplied to the the List, but its known superclass of Vehicle can be returned by the list. 也就是说,无法将未知类型T提供给列表,但是列表可以返回其已知的Vehicle超类。

By way of example, given your method: 通过示例,给出您的方法:

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

you could conceivably invoke: 您可以想像地调用:

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

which you intuit should be permitted. 应该允许你这样做。 However, since addCars knows the list only as "some subtype of Vehicle ", it can't be allowed to add objects to the list since the following invocation would then be just as valid: 但是,由于addCars仅将列表作为“ Vehicle某些子类型”知道,因此不允许将对象添加到列表,因为以下调用将同样有效:

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

whereby it becomes clear that it must be as mistake to try to add Car objects to a List under the guise of being a list of Vehicle objects. 由此变得清楚,它必须是错误的做法车的物体所列表假借添加到列表Vehicle对象。

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

should be 应该

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

This will fix the compile time error. 这将修复编译时错误。

EDIT: Read here . 编辑: 在这里阅读。

The type of parameter is ? extends Vehicle 参数的类型是? extends Vehicle ? extends Vehicle , which means an unknown subtype of Vehicle . ? extends Vehicle ,这意味着一个未知的亚型Vehicle Since we don't know what type it is, we don't know if it is a supertype of Car ; 由于我们不知道它是什么类型,所以我们不知道它是否是Car的超类型; it might or might not be such a supertype, so it isn't safe to pass a Car there. 它可能是也可能不是这种超类型,因此在那儿通过Car并不安全。

Read page 7 of this tutorial . 阅读本教程的第7页。

When you say <? extends Vehicle> 当你说<? extends Vehicle> <? extends Vehicle> it means it can be of any type which extends vehicle. <? extends Vehicle>这意味着它可以是任何类型的扩展车辆。 That means somebody can pass List and it will accept it. 这意味着有人可以通过List,它将接受它。 Now a List<Spaceship> can not have new Car() as one of his item. 现在List<Spaceship>不能将新的Car()作为他的一项。 So to avoid these errors, you are not allowed to add any object inside the list if you have used wildcard expression. 因此,为避免这些错误,如果使用通配符表达式,则不允许在列表内添加任何对象。

You can either use: 您可以使用:

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

(which means the caller is to pass a list of objects that are Vehicle or a super type) (这意味着调用方将传递车辆或超级类型的对象列表)

or 要么

private static void addCars(List<Vehicle> vcls)

The Get and Put Principle Problem: 获取和放置原则问题:

you can try this 你可以试试这个

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

its like; 就像是;

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

and for wildcard for 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 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

if following could be possible.. 如果可能的话..

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

then you could call addCars in this way: 那么您可以通过以下方式调用addCars:

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

Using prince get and put in wildcard If wildcard with Extends ---> Using get method If wildcard with Super ----> Using put method Here , you want to add value into List ( meaning put method) .You can change code 使用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