通配符扩展

时间:2013-10-13 10:29:18

标签: java generics wildcard

代码:

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>

问题我是否理解通配符现在正常工作的原理?

2 个答案:

答案 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的对象(如上所述)。

相关问题