ArrayList <? super Number> 和 Double

5

来自http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ103:

带有下限的通配符形式为“?super Type”,代表所有类型家族,这些类型都是Type的超类型,包括Type类型本身。Type被称为下限。

所以为什么呢?

ArrayList<? super Number> psupn1 = new ArrayList<Number>();
psupn1.add(new Double(2));

编译?

Double 不是 Number 的超类型,而是 Number 的子类...

编辑 1:

    ArrayList<? super Number> pextn1 = new ArrayList<Number>();
    psupn1.add(new Integer(2));
    psupn1.add(new Double(2));
    psupn1.add(new Float(2));
    for(Number n : psupn1){ // [Invalid] Number should be change to
    // Object even if I can only add subtype of Number??

    }

2
这可能会有所帮助:https://dev59.com/7nE85IYBdhLWcg3wikK- - James 'Cookie' Cook
4个回答

6

你可以添加一个 Double,因为无论类型参数 E 是什么,它都保证是 Number 或者是一个超类型...这意味着你一定可以将 Double 转换成 E。你不能这样做:

Number x = psupn1.get(0);

想一想,试着创建逻辑上可以分解这个的列表。例如,你不能使用:

// Invalid
ArrayList<? super Number> psupn1 = new ArrayList<Integer>();
psupn1.add(new Double(2));

由于 Integer 既不是 Number 也不是其超类,而是其子类。因此,您可以这样编写:

// Valid
ArrayList<? extends Number> psupn1 = new ArrayList<Integer>();

...因为这是相反的情况。此时,您可以编写:

Number x = psupn1.get(0);

因为列表中的任何元素都可以转换为Number,所以这是有保证的。关键在于需要进行哪种类型的转换 - 转换为通用类型参数还是从它转换过来。

谢谢您的解释,但是您能给我解释一下我刚写的“Edit 1”吗?我还是不太明白。 - JohnJohnGa

5

莫里斯·纳夫特林(Maurice Naftalin)和菲利普·沃德勒(Philip Wadler)在Java Generics and Collections中最好地解释了它:

获取和放置原则:当您仅从结构中获取值时,请使用扩展通配符;当您仅将值放入结构中时,请使用超级通配符;当您既获取又放置时,请不要使用通配符。


+1 这个规则很容易理解! - JohnJohnGa
PECS(生产者扩展,消费者超级) - newacct

0

对于任何类型的X,您只能从List<? extends X>中检索X,并且只能将X插入到List<? super X>中。但是,您可以将X插入和检索到/从List<X>中。

在这里,您可以将任何Number插入到此实例的List<? super Number>中,当然,您的所有示例new Integer(2)new Double(2)等都是Number的实例,因此它们可以放入列表中。

但是,如果您在此列表上调用.get(),则不能假定它仅包含数字。要了解原因,请注意,这是合法的Java代码:

    List<Serializable> serializables = new ArrayList<Serializable>();
    serializables.add("You know String implements Serializable!");
    List<? super Number> list = serializables;
    Object o = list.get(0);  // Object is your best guess about the result's type

此外需要注意的是,.get().add() 并没有什么神奇之处。实际上,这一切发生都是因为如果您检查java.util.List<E>的定义,您会发现.get() 返回一个E,而add()E作为参数传递。

哇,这是说Serializable是Number的子类吗? - JohnJohnGa
不是的,实际上是因为Serializable是Number的超类 - Saintali

0

List<? super Number> 的意思是:我们保证您可以将 Number 或任何 Number 的子类放入此 List 中。它并不保证您从中得到什么。请考虑以下情况:

void <T> addToList(List<? super T> list, T... things) {
    for (T t: things) {
        list.add(t);
    }
}

void <T> printList<List<? super T> list) {
    for (T t: list) { // doesn't compile
        System.out.println(t);
    }

    for (Object o: list) { // just fine
        System.out.println(o);
    }
}

public static void main(String[] args) {
    List<Object> objects = new ArrayList<Object>();

    // 1, 2.0f, 3.0 autoboxed to Integer, Float, Double
    addToList(objects, "a string", new Object(), 1, 2.0f, 3.0); // just fine
    printList(objects);

    List<Number> numbers = new ArrayList<Number>();
    addToList(numbers, "a string", new Object(), 1, 2.0f, 3.0); // doesn't compile
    addToList(numbers, 1, 2.0f, 3.0); // just fine
    printList(numbers);

    List<Integer> ints = new ArrayList<Integer>();
    addToList(ints, 1, 2.0f, 3.0); // doesn't compile
    addToList(ints, 1, 2, 3); // just fine
    printList(ints);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接