猿问

试图理解超级通配符在Java泛型中的作用

我正在阅读这篇文章来了解通配符在泛型中的作用super。我明白如何extends运作,但我很难理解super


我有一个扩展ClassA,使ClassB超ClassA类ClassB。


如果我正确理解这篇文章,则将<? super ClassB>允许任何属于ClassB.


我有以下代码


GenericMethod.java


public class GenericMethod<T> {


    private List<T> list;


    public GenericMethod() {

        list = new ArrayList<>();

    }


    public void add(T t) {

        list.add(t);

    }


    public T get(int index) {

        return list.get(index);

    }

}

Driver.java


public class Driver {


    public static void main(String[] args) {


        GenericMethod<? super ClassB> genericMethod = new GenericMethod<>();


        ClassA classA = new ClassA();

        genericMethod.add(classA); // Compile-time error here


    }


}

错误


The method add(capture#1-of ? super ClassB) in the type GenericMethod<capture#1-of ? super ClassB> is not applicable for the arguments (ClassA)


我不明白我哪里错了。当我实例化该类时GenericMethod,我已经声明它将接受任何带有声明的超类型值。因此,类内部应该接受所有扩展的类。ClassB<? super ClassB>TGenericMethodClassB


那么为什么该add方法会抛出编译时错误呢?该方法不应该add已经知道它正在传递一个完全兼容的类型吗?


温温酱
浏览 100回答 3
3回答

一只甜甜圈

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

慕妹3146593

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

慕桂英3389331

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

相关分类

Java
我要回答