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辆汽车的宇宙飞船列表.显然没有好处.
这是指向您收到编译错误的原因的指针.特别,
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是不安全的.
提供的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.