代码:
List<? extends Integer> ints= new ArrayList<Integer>();
ints.add(new SomeType());
我试图解释为什么我们无法正式添加ints
。请检查正确性。
编译器始终将问号与匿名类型CAP#n
匹配,其中n
是源代码中通配符声明的序号。事实上,带有extends的这个wildrcard意味着编译器在内部将CAP#1
(在这种情况下)分配给匿名类型null
。但我不确定这个原因。考虑
List<? super Integer> ints= new ArrayList<Integer>();
ints.add(new Object());//error
在这种情况下,我们让编译器在内部创建一个标记为CAP#2
的新匿名类型,这样只有所有Integer's
超类型的实例才是“CAP#2
的实例。” p>
问题我是否理解通配符现在正常工作的原理?
答案 0 :(得分:2)
让我们尝试从java.util.List
中查看不同视图中呈现的问题public interface List<E> extends Collection<E> {
*
*
boolean add(E e);
*
*
}
当您指定List<? extends Integer>
时,添加()的参数会变为'?扩展整数'。根据该描述,编译器无法知道那里需要哪个整数的特定子类型,因此它不会接受任何类型的整数。
List<? super Integer>
的用法告诉编译器可以将所有超级类型整数添加到列表中,添加其他类型将违反静态类型安全。
因此,你可以开始考虑子类型和超类型界限,你可以如何“写”(传递给方法)到泛型类型,并从泛型类型“读取”(从方法返回)。
基本上你的技术描述是正确的,但我认为从静态类型安全的角度来看,我的解释更合理。
希望它对你有所帮助。
答案 1 :(得分:0)
让我们忽略这样一个事实,即Integer在本次讨论中是最终的。
当您为变量提供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。它只知道未知类型是Integer的一些超类型,内部称为CAP#2,因此任何整数或任何子类型 S的整数都可以添加到列表中(因为没有无论CAP#2是什么,S是Integer的子类型,它是CAP#2的子类型,因此add( CAP#2, int )
将接受第一个参数中的S。)
相比之下,您尝试使用Object调用该方法,该不是 Integer的子类型。编译器拒绝尝试传递需要CAP#2的对象(如上所述)。