16

假设 classB继承自 class A。以下是合法的Java:

List<A> x;
List<? super B> y = x;

根据规范,这意味着List<A>assignsTo List<? super B>。但是,我很难找到说明这是合法的规范部分。特别是,我相信我们应该有子类型关系

List<A>  <:  List<? super B>

但是 Java 8 规范的第 4.10 节将子类型关系定义为直接超类型关系的传递闭包S >1 T,并且它根据计算一组超类型的有限函数来定义直接超类型关系T。没有可以在输入List<A>上产生的有界函数,List<? super B>因为可能有任意数量的Bs 继承自A,因此规范的子类型定义似乎对超级通配符不适用。关于“类和接口类型之间的子类型化”的第 4.10.2 节确实提到了通配符,但它只处理通配符出现在潜在子类型中的另一个方向(这个方向适合计算的直接超类型机制)。

问题:规范的哪一部分说上述代码是合法的?

动机是编译器代码,因此仅凭直觉理解为什么它是合法的或想出一个处理它的算法是不够的。由于 Java 中的一般子类型问题是无法确定的,我想处理与规范完全相同的情况,因此想要处理这种情况的规范部分。

4

2 回答 2

11

List<? super B>被定义为List<A>§4.10.2的超类型。类和接口类型之间的子类型化

参数化类型的直接超类型(其中(1 ≤ i ≤ n)是一个类型)如下:C<T1,...,Tn>Ti

  • D<U1 θ,...,Uk θ>, 其中是 的直接超类型并且是替换。D<U1,...,Uk>C<T1,...,Tn>θ[F1:=T1,...,Fn:=Tn]

  • C<S1,...,Sn>,其中包含(1 ≤ i ≤ n) ( §4.5.1 )。Si Ti

让和。根据第二个项目符号,是if contains的超类型。C<T1,...,Tn> = List<A>C<S1,...,Sn> = List<? super B>List<? super B>List<A>? super B A

包含关系在§4.5.1中定义。类型参数和通配符

如果根据以下规则的自反和传递闭包(其中表示子类型(第 4.10 节)) ,表示的类型集可证明是 表示的类型集的子集,则称类型参数包含另一个类型参数。 :T1T2T2 <= T1T2T1<:

  • ? extends T <= ? extends S如果T <: S

  • ? super T <= ? super S如果S <: T

  • T <= T

  • T <= ? extends T

  • T <= ? super T

通过第二个项目符号,我们可以看到? super B 包含 ? super A. 通过最后一个项目符号,我们看到? super A 包含 A. 传递地,我们因此知道? super B 包含 A

于 2015-04-24T21:01:21.960 回答
4

将列表分配给 < 是什么意思?super B>究竟是什么意思?

考虑以下程序:

public class Generics {
    static class Quux { }
    static class Foo extends Quux { }
    static class Bar extends Foo { }

    public static void main(String... args) {
        List<Foo> fooList = new ArrayList<>();
        // This is legal Java
        List<? super Bar> superBarList = fooList;
        // So is this
        List<? super Foo> superFooList = fooList;

        // However, this is *not* legal Java
        superBarList.add(new Quux());

        // Neither is this
        superFooList.add(new Quux());

        // Or this:
        superFooList.add(new Object());

        // But this is fine
        superFooList.add(new Foo());
    }
}

为什么会这样?首先,让我们谈谈JLS怎么说

来自 JLS,§4.5.1:

如果 T2 表示的类型集在以下规则的自反和传递闭包下可证明是 T1 表示的类型集的子集,则称类型参数 T1 包含另一个类型参数 T2,写作 T2 <= T1 (其中 <: 表示子类型(§4.10)):

  • ? 超级 T <= ? 超级 S 如果 S <: T
  • T <= ? 超级T

因此,T <= ? 超级 S 如果 S <: T。


... 但是,这是什么意思?

如果我不能添加new Quux(), 或new Object()? List<? super Foo> 意味着这个列表只包含严格超类型的元素Foo,但我不知道碰巧是哪种类型。换句话说,我可以将列表声明为这样的 type,但我不能向其中添加我不是 100% 确定是 type 的元素? super FooQuux可能是那种类型,但也可能不是那种类型。

出于这个原因,将 a 分配为List<Foo>不允许List<? super Bar>堆污染,最终不是问题。

延伸阅读:AngelikaLanger 通用解释的相关部分

于 2015-04-24T21:01:35.013 回答