1

如果这种类型应该能够添加 Apple 的任何超类型,为什么不能将新对象添加到列表中?

import java.util.List;
import java.util.ArrayList;
class Apple{}
public class Macintosh extends Apple {
    public static void main(String[] munch){
        List<Apple> a = new ArrayList<Apple>();
        basket(a);
    }
    static void basket(List<? super Apple> list){ list.add(new Object());}
}

如果我们将参数更改为 List 列表,它当然可以工作:s


class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}
public class Mixer<A extends Animal>{
    public <C extends Cat> Mixer<? super Dog> useMe(A a, C c){
        return new Mixer<Animal>();
    }
}

如果以前编译器不知道对象的Class,为什么我们可以在返回时使用Mixer,现在是因为是一个类?¿?

4

3 回答 3

0

您正在尝试将不是 Apple 的东西添加到 Apple 列表中(对象不是 Apple 的子类)。因此,编译错误。

于 2012-10-18T12:22:38.080 回答
0

您不能将 Apple 及其子类型以外的任何内容添加到 aList<? super Apple>中,因为 ? 表示“某种类型”而不是“任何类型”。它是某种类型的实例列表,要么是 Apple,要么是 Apple 的超类型,并且无法知道具体是哪一种类型?方法。

如果可以的话,我建议避免这些限制,它们经常没有帮助,而且很容易导致混乱。

于 2012-10-18T13:08:01.793 回答
0

正如其他人所指出的,通配符类型是存在量化的类型,而不是普遍量化的。这意味着,当使用通配符类型的变量时,您可以在写入时分配您希望的任何匹配类型,但在从变量读取时不能假定任何特定类型。写作时,你控制选择,但阅读时,别人为你选择。

更多详情

  • 写作时您可以完全控制。return方法的返回值在语句中写入其主体内部。它的参数被写入您调用该方法的位置。您可以愉快地将特定类型分配给通配符return语句或将特定类型传递给通配符方法参数。

  • 阅读时,选择是强加给你的。在调用方法的地方读取方法的返回值。它的参数在它的主体中读取,您可以在其中定义方法。basket()当您尝试使用pass 时,您会遇到内部困难list

如果我们将参数更改为 List 列表,它当然可以工作:s

那是一个完全不同的故事。List是原始类型。正如您自己发现的那样,您可以做不安全的事情,例如在使用原始类型时将 anOrange或任何其他内容添加Object到 a中。List<Apple>Java 支持原始类型主要是出于历史原因,例如向后兼容性。它们在设计上是不健全的。如果您以这种方式使用原始类型,您可能会在ClassCastException以后获得 s:

    List<Apple> a = new ArrayList<Apple>();
    basket(a);
    for (Apple apple : a) {     // Is not assignable to Apple, throws ClassCastException
        System.out.println(apple);
    }
于 2012-10-19T15:23:53.130 回答