【问题标题】:Wildcard in a list - Java Generics [duplicate]列表中的通配符 - Java 泛型 [重复]
【发布时间】:2014-09-07 17:20:13
【问题描述】:
ArrayList<? extends A> array = new ArrayList<A>();
array.add(new A());

为什么不编译?

【问题讨论】:

  • 为了将来参考,您还应该发布编译器错误。

标签: java generics


【解决方案1】:

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&lt;EvenNumber&gt;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。

【讨论】:

    【解决方案2】:

    由于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。当然,也许我错过了这样做的理由。
    【解决方案3】:

    您不能将A 添加到List&lt;? extends A&gt;。为了解决您的问题,您只需将列表声明为

    ArrayList<A> array = new ArrayList<A>();
    

    请注意,最好使用接口进行声明:

    List<A> array = new ArrayList<A>();
    

    这使您可以在以后轻松更改具体类型,因为您只需要进行一次更改。

    【讨论】:

    • 声明没有问题,array.add(new A()); 是问题,因为A 对象无法添加到List&lt;? extends A&gt;
    • @Henry 嗯...我明白了。即使我对错误原因的解释无效,我的解决方案仍然有效。
    • 假设 B 扩展 A 并且你有 List&lt;? extends A&gt; l = new ArrayList&lt;B&gt;()。很明显l.add(new A()) 会违反列表的完整性。
    猜你喜欢
    • 2016-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-02
    相关资源
    最近更新 更多