【问题标题】:Trying to understand the role of super wildcard in Java Generics试图理解超级通配符在Java泛型中的作用
【发布时间】:2019-10-16 19:49:36
【问题描述】:

我正在通过this 文章了解super 通配符在泛型中的作用。我了解extends 的工作原理,但我很难理解super

我有一个由ClassB扩展ClassA,使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&lt;capture#1-of ? super ClassB&gt; is not applicable for the arguments (ClassA)

我不明白我哪里出错了。当我实例化GenericMethod 类时,我已经声明它将接受ClassB超类型 的任何值,并声明&lt;? super ClassB&gt;。因此,GenericMethod 类中的T 应该接受ClassB 扩展的所有类。

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

【问题讨论】:

    标签: java generics extends super


    【解决方案1】:

    ? super 子句是一个下限通配符。但是限制是在推断的类型参数上,而不是对可以传递给采用该泛型类型参数的方法的参数类型的限制。

    当您说&lt;? super ClassB&gt; 时,您表示类型参数可以是ClassB 或任何超类型,例如ClassAObject

    编译器必须将add 方法视为可以是以下任何签名:

    add(Object t)
    add(ClassA t)
    add(ClassB t)
    

    (如果ClassA直接继承自另一个类而不是Object,则可能有其他类型。

    编译器必须拒绝 ClassA 作为 add 的参数,因为类型参数可以推断为 ClassB。将GenericMethod&lt;ClassB&gt; 分配给genericMethod 变量是合法的。

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

    但是将ClassA 传递给需要ClassB 的方法是没有意义的。

    事实上,这就是菱形运算符所推断的 - ClassB

    您的困惑在于两个概念的混淆:允许哪些类型参数以及在使用类型参数的方法中允许哪些类型的对象。使用通配符会限制类型参数,但该方法仍然接受类型参数或子类型的类型。

    【讨论】:

    • 能否请您详细说明您的意思 编译器必须拒绝 ClassA 作为要添加的参数,因为类型参数可能被推断为 ClassB
    • 如果类中的方法只接受下限类或子类型的方法,我不理解使用下限类型参数的意义。
    • 请参阅What is PECS?。将类型参数类型化为泛型类型参数(又使用? super 定义)的方法保证能够接受类型化为下限或下限的参数。在这里,add 方法只保证能够接受ClassB 或更低类型的参数,而不管实际的类型参数是什么。
    【解决方案2】:

    通过声明GenericMethod&lt;? super ClassB&gt;,您声明该类型是未知类型,它是ClassB(或ClassB 本身)的超类。并要求编译器只允许将这种未知类型的子类型添加到列表中。

    编译器知道的唯一兼容子类型是 ClassB 和 ClassB 的任何子类型。创建实例时,通常最好避免使用通配符。

    对于方法参数,通配符让您可以更灵活地接受哪些参数。使用 PECS (producer=extends, consumer=super) 来确定使用哪个。 See Slide 5

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-02
      • 1970-01-01
      相关资源
      最近更新 更多