3 回答

TA贡献1836条经验 获得超5个赞
通过声明,GenericMethod<? super ClassB>
您将声明该类型是未知类型,它是 ClassB(或 ClassB 本身)的超类。并要求编译器只允许将这种未知类型的子类型添加到列表中。
编译器知道的唯一兼容子类型是 ClassB 和 ClassB 的任何子类型。创建实例时,通常最好避免使用通配符。
对于方法参数,通配符使您可以更灵活地接受哪些内容。使用PECS
(生产者=扩展,消费者=超级) 来确定使用哪个。

TA贡献1820条经验 获得超9个赞
该? super
子句是下限通配符。但界限是在推断的类型参数上,而不是对可以传递给采用该泛型类型参数的方法的参数类型的限制。
当您说 时<? super ClassB>
,您表明类型参数可以是ClassB
或 任何超类型,例如ClassA
或Object
。
编译器必须将该add
方法视为可以是以下任何签名:
add(Object t) add(ClassA t) add(ClassB t)
ClassA
(如果直接从另一个类继承而不是继承的话,可能还有其他类型Object
)。
编译器必须拒绝将 aClassA
作为 的参数,add
因为类型参数可能被推断为ClassB
。GenericMethod<ClassB>
将 a 分配给变量是合法的genericMethod
。
GenericMethod<? super ClassB> genericMethod = new GenericMethod<ClassB>();
但是能够将 a 传递ClassA
给需要 a 的方法是没有意义的ClassB
。
事实上,这就是菱形算子 - 的推断ClassB
。
您的困惑在于两个概念的合并:允许哪些类型参数以及使用类型参数的方法中允许哪些类型的对象。使用通配符会限制类型参数,但该方法仍然接受类型参数或子类型的类型。

TA贡献2036条经验 获得超8个赞
我承认这有点违反直觉,但从编译器的角度来看,这是有道理的。
假设您以这种方式定义一个列表:
List<? super ClassB> myList;
你得到了
ClassB extends ClassA; ClassC extends ClassA;
你能做吗?
ClassA a = new ClassC(); myList.add (a);//does not compile
不,有几个原因。首先是因为最终在运行时,参数化类型变成了具体的单一类型。因此,实际上您不能将A 类对象与 B 类对象混合。
其次,因为编译器必须检查您插入的任何内容是否可强制转换为ClassB。因此,只需添加一个 ClassB 对象(或他的子类型),即可履行合同。但事实并非总是如此!ClassA 对象不能是 ClassB 的超类型。
即对象“a”实际上是一个ClassC对象!
这就是为什么你不能添加超类型。所以它只允许您添加 ClassB 对象 - 或子类型 -
如果将对象“a”转换为 ClassB,则会编译,但在运行时会失败并出现ClassCastException:
ClassA a = new ClassC(); myList.add((ClassB) a); //Compiles but fails at runtime
同时,通配符与super结合,可以作为方法的形参。IE:
void printValues (List<? super ClassB> list)
这种方式允许对超级 B 类集合的处理进行一般化,从而使以下调用成为可能:
List<Object> lo; List<ClassA> la; ... printValues (lo); printValues (la);
在本例中,定义使用通配符,例如 <? super ClassB>,更有意义一些。
添加回答
举报