[英]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.