Java 泛型通配符问题:List<? 扩展 A>
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5495383/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java Generics WildCard Question: List<? extends A>
提问by Saher Ahwal
Let's say I have these classes : Vehicle, Car and Spaceship:
假设我有这些类:Vehicle、Car 和 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. right?
为什么会出现编译时错误??我知道 List 是扩展 Vehicle 的任何 X 的 List 的超类型。对?
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).
编辑:我得到的错误(编译时):类型列表中的方法 add(capture#2-of ? extends Vehicle) 不适用于参数 (Car)。
回答by Philip JF
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>
. 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.
方法参数在子类型中是逆变的,并且根据通配符的定义,对于每个T
扩展Vehicle
Foo<T>
的类型都是 的子类型Foo<* extends Vehicle>
。这意味着当您只关心返回类型时通配符非常有用,但是当您想将类型的值传递给方法时,在这种情况下就不起作用了。
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. Clearly no good.
如果您的代码要编译,l
则将是包含 3 辆汽车的宇宙飞船列表。显然不好。
回答by Amir Afghani
Here'sa 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. (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.
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:
List 是一个有界通配符的例子。这 ?代表未知类型,就像我们之前看到的通配符一样。但是,在这种情况下,我们知道这种未知类型实际上是 Shape 的子类型。(注意:它可以是 Shape 本身,也可以是某个子类;它不需要从字面上扩展 Shape。)我们说 Shape 是通配符的上限。
像往常一样,使用通配符的灵活性需要付出代价。代价是现在在方法体中写入形状是非法的。例如,这是不允许的:
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 ? extends Shape-- an unknown subtype of Shape. Since we don't know what type it is, we don't know if it is a supertype of Rectangle; it might or might not be such a supertype, so it isn't safe to pass a Rectangle there.
您应该能够弄清楚为什么不允许使用上面的代码。shape.add() 的第二个参数的类型是?扩展形状——形状的未知子类型。由于我们不知道它是什么类型,所以我们不知道它是否是 Rectangle 的超类型;它可能是也可能不是这样的超类型,因此在那里传递 Rectangle 是不安全的。
回答by Lawrence Dol
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; 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. Only methods which don't involve T
as an argumentcan be invoked.
提供的 List 是一些特定类型的 Vehicle 的列表(其中,为了论证起见,我们将该类型称为T
),但该特定类型T
未知;它可能是List<Vehicle>
、List<Car>
等。因此,由于列表的特定泛型类型未知,因此不允许调用任何需要特定T
作为参数的方法。只有不涉及方法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. On the other hand, the list canbe read, but with the returned objects only known as Vehicle
.
在 List 的情况下,这样做的实际结果是,这可以防止将任何内容添加到列表中——该列表是不可写的。另一方面,可以读取列表,但返回的对象仅称为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
不能提供给List,但是它的已知超类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.
很明显,试图以对象列表为幌子将 Car 对象添加到 List 肯定是错误的Vehicle
。
回答by Prince John Wesley
回答by Thomas Mueller
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)
(这意味着调用者要传递一个包含 Vehicle 或超类型的对象列表)
or
或者
private static void addCars(List<Vehicle> vcls)
回答by Ankit Bansal
When you say <? extends Vehicle>
it means it can be of any type which extends vehicle. That means somebody can pass List and it will accept it. Now a List<Spaceship>
can not have new Car() as one of his item. So to avoid these errors, you are not allowed to add any object inside the list if you have used wildcard expression.
当你说<? extends Vehicle>
这意味着它可以是任何类型的扩展车辆。这意味着有人可以通过 List 并且它会接受它。现在List<Spaceship>
不能有 new Car() 作为他的项目之一。因此,为了避免这些错误,如果您使用了通配符表达式,则不允许在列表中添加任何对象。
回答by mgv
The type of parameter is ? extends Vehicle
, which means an unknown subtype
of Vehicle
. Since we don't know what type it is, we don't know if it is a supertype
of Car
; it might or might not be such a supertype, so it isn't safe to pass a
Car
there.
参数的类型是? extends Vehicle
,这意味着 的未知子类型Vehicle
。由于我们不知道它是什么类型,因此我们不知道它是否是 的超类型Car
;它可能是也可能不是这样的超类型,因此在Car
那里传递 a 是不安全的
。
Read page 7 of this tutorial.
阅读本教程的第 7 页。
回答by Andrea Scarafoni
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);
回答by user2663609
The Get and Put Principle Problem:
Get 和 Put 原则问题:
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
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
回答by Le Thanh Tung
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 和put in wildcard If wildcard with Extends ---> Using get method If wildcard with Super ----> Using put method 这里,你想把值添加到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());
}