【发布时间】:2014-09-07 17:20:13
【问题描述】:
ArrayList<? extends A> array = new ArrayList<A>();
array.add(new A());
为什么不编译?
【问题讨论】:
-
为了将来参考,您还应该发布编译器错误。
ArrayList<? extends A> array = new ArrayList<A>();
array.add(new A());
为什么不编译?
【问题讨论】:
Java 教程中的相关部分 (http://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html):
由 List 定义的列表可以非正式地认为是 只读,但这不是严格的保证。假设你有 以下两个类:
class NaturalNumber { private int i; public NaturalNumber(int i) { this.i = i; } // ... } class EvenNumber extends NaturalNumber { public EvenNumber(int i) { super(i); } // ... }考虑以下代码:
List<EvenNumber> le = new ArrayList<>(); List<? extends NaturalNumber> ln = le; ln.add(new NaturalNumber(35)); // compile-time error因为
List<EvenNumber>是List<? extends NaturalNumber>的子类型,所以可以将le 分配给ln。但是你不能使用 ln 添加 自然数到偶数列表。可以对列表进行以下操作:
- 您可以添加null。
- 您可以调用 clear。
- 您可以获得迭代器并调用remove。
- 您可以捕获通配符并写入从列表中读取的元素。
可以看到List定义的列表是 不是严格意义上的只读,但您可能会认为 因为你不能存储一个新元素或改变一个 列表中的现有元素。
另一个相关的解释可以在这里找到(他的链接也解释了通配符的问题-http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html):
...
但是,向其中添加任意对象是不安全的:
Collection<?> c = new ArrayList<String>(); c.add(new Object()); // Compile time error由于我们不知道 c 的元素类型代表什么,所以我们不能 向其中添加对象。 add() 方法接受类型 E 的参数, 集合的元素类型。当实际类型参数为 ? 时, 它代表某种未知类型。我们传递给 add 的任何参数都会 必须是这种未知类型的子类型。因为我们不知道什么 类型,也就是说,我们不能传入任何东西。唯一的例外是 null, 它是每个类型的成员。
另一方面,给定一个 List,我们可以调用 get() 并使用 结果。结果类型是未知类型,但我们总是知道 它是一个对象。因此,分配结果是安全的 get() 到 Object 类型的变量或将其作为参数传递,其中 需要类型 Object。
【讨论】:
由于we don't know数组的元素类型代表什么,我们不能给它添加对象。
改为使用临时列表:
ArrayList<A> tempArray = new ArrayList<A>();
tempArray.add(new A())
tempArray.add(new A())
ArrayList<? extends A> array = tempArray;
如何使用它的示例
//车辆
public abstract class Vehicle {
}
//汽车
public class Car extends Vehicle {
}
//HandlerVehicle
public class HandlerVehicle {
private List<? extends Vehicle> _vehicles;
public void addVehicles(List<? extends Vehicle> vehicles) {
_vehicles = vehicles;
//perform operations with Vehicle objects
}
}
//处理车
public class HandlerCar {
private HandlerVehicle _handlerVehicle;
private List<Car> _cars;
public HandlerCar() {
_cars = getCars();
_handlerVehicle = new HandlerVehicle();
_handlerVehicle.addVehicles(_cars);
}
private List<Car> getCars() {
return new ArrayList<Car>();
}
}
【讨论】:
ArrayList 并将其分配给引用genericArray。然后通过分配genericArray 来引用与array 相同的列表,从而丢弃新列表。
tempArray,因此实际上并不需要array。当然,也许我错过了这样做的理由。
您不能将A 添加到List<? extends A>。为了解决您的问题,您只需将列表声明为
ArrayList<A> array = new ArrayList<A>();
请注意,最好使用接口进行声明:
List<A> array = new ArrayList<A>();
这使您可以在以后轻松更改具体类型,因为您只需要进行一次更改。
【讨论】:
array.add(new A()); 是问题,因为A 对象无法添加到List<? extends A>
B 扩展 A 并且你有 List<? extends A> l = new ArrayList<B>()。很明显l.add(new A()) 会违反列表的完整性。