0

代码:

List<? extends Integer> ints= new ArrayList<Integer>();
ints.add(new SomeType());

我试图解释为什么我们不能ints正式添加。请检查正确性。

编译器总是将问号匹配到匿名类型CAP#n,其中n是源代码中通配符声明的序号。从这个带有extends的通配符意味着编译器在内部分配CAP#1(在这种情况下)只分配给匿名类型的null. 但我不确定这个原因。考虑

List<? super Integer> ints= new ArrayList<Integer>(); 
ints.add(new Object());//error

在这种情况下,我们让编译器在内部创建一个新的匿名类型,标记为,以便所有超类型的CAP#2唯一实例是“实例的” 。Integer'sCAP#2

问题我现在了解通配符的工作原理正确吗?

4

2 回答 2

2

让我们尝试从 java.util.List 以不同的视角看待所提出的问题

public interface List<E> extends Collection<E> {
    *
    *

    boolean add(E e);

    *
    *
}

当您指定时, add()List<? extends Integer>的参数变为'? 扩展整数'。从该描述中,编译器无法知道那里需要哪种特定的Integer子类型,因此它不会接受任何类型的Integer

的用法告诉编译器可以将所有超类型的IntegerList<? super Integer>添加到列表中,添加其他类型将违反静态类型安全。

因此,您可以根据如何“写入”(传递给方法)到泛型类型以及如何从泛型类型“读取”(从方法返回)来考虑子类型和超类型的界限。

基本上您的技术描述是正确的,但我认为从静态类型安全的角度来看,我的解释更合理。

希望它可以帮助你。

于 2013-10-13T11:58:41.083 回答
0

为了讨论的目的,让我们忽略 Integer 是 final 的事实。

当您为变量提供 的类型时List< ? extends Integer >,编译器不允许您调用任何具有泛型类型参数作为方法参数的 List 方法。这些参数处于所谓的逆变位置,如果编译器允许你做你想做的事情,Java 的类型系统将比现在更不健全。编译器只知道 List 的元素类型是 Integer 的某个未知子类型,内部称为 CAP#1。现在尝试add( CAP#1, int )使用任何东西作为第一个参数进行调用将失败。唯一的例外是null,因为与 Java 中的任何其他值不同,null 是每个引用类型的成员,所以它必须是 CAP#1 的成员。请注意,编译器允许您调用List< ? extends Integer >没有泛型类型输入但可能产生泛型类型输出。

与@Maxim Kirilov 给出的答案相反,当您为变量提供类型时List< ? super Integer >,编译器不允许您添加任何整数超类型。它所知道的只是未知类型是 Integer 的某个超类型,内部称为 CAP#2,因此任何 Integer 或 Integer 的任何子类型S 都可以添加到列表中(因为无论 CAP#2 结果如何也就是说,S 是 Integer 的子类型,它是 CAP#2 的子类型,因此add( CAP#2, int )将在第一个参数中接受 S)。

相比之下,您尝试使用不是Integer 子类型的 Object 调用该方法。编译器拒绝传递需要 CAP#2 的对象的尝试(如上所述)。

于 2013-10-13T14:45:27.860 回答