关于泛型:Java规范在哪里说List< T>

Where does the Java spec say List<T> assigns to List<? super T>?

假设类B继承自类A。以下是合法Java:

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

就规范而言,这意味着List转让给List。但是,我很难找到规范中说明这是合法的部分。特别是,我认为我们应该有亚型关系

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

但是Java 8规范的第4.10节定义了子类型关系作为直接超关系EDCOX1〔4〕的传递闭包,并且它定义了直接超关系,它是用一个有限函数来计算EDOCX1×5的一组超类型。在输入List上没有可以生成List的有界函数,因为可能有任意数量的B继承自A,所以规范的子类型定义似乎对超级通配符是分解的。关于"类和接口类型之间的子类型"的第4.10.2节确实提到了通配符,但它只处理通配符出现在潜在子类型中的另一个方向(该方向适合于计算的直接父类型机制)。

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

编译器代码的动机是,所以不足以理解为什么它是直观合法的,或者想出一个处理它的算法。由于Java中的泛型子类型问题是不可判定的,所以我希望处理与SPEC完全相同的情况,因此需要处理该情况的规范的一部分。


List由§;4.10.2定义为List的父类型。类和接口类型之间的子类型:

The direct supertypes of the parameterized type C1,...,Tn>, where Ti
(1 ≤ i ≤ n) is a type, are all of the following:

  • D1 θ,...,Uk θ>, where D1,...,Uk> is a direct supertype of C1,...,Tn> and θ is the substitution [F1:=T1,...,Fn:=Tn].

  • C1,...,Sn>, where Si contains Ti (1 ≤ i ≤ n) (§4.5.1).

C1,...,Tn> = ListC1,...,Sn> = List。根据第二颗子弹,如果? super B含有AListList的超类型。

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

A type argument T1 is said to contain another type argument T2, written T2 <= T1, if the set of types denoted by T2 is provably a subset of the set of types denoted by T1 under the reflexive and transitive closure of the following rules (where <: denotes subtyping (§4.10)):

  • ? extends T <= ? extends S if T <: S

  • ? super T <= ? super S if S <: T

  • T <= T

  • T <= ? extends T

  • T <= ? super T

通过第二颗子弹,我们可以看到? super B含有? super A。最后一颗子弹,我们看到? super A含有A。因此,我们知道? super B包含A


将列表分配给什么?超级B>实际上是什么意思?

考虑以下程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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,?4.5.1:

A type argument T1 is said to contain another type argument T2, written T2 <= T1, if the set of types denoted by T2 is provably a subset of the set of types denoted by T1 under the reflexive and transitive closure of the following rules (where <: denotes subtyping (§4.10)):

  • ? super T <= ? super S if S <: T
  • T <= ? super T

因此,t<=?super s如果s<:t.

…但这意味着什么?

如果我不能添加一个new Quux(),或者一个new Object()List意味着这个列表只包含对Foo是严格超类型的元素,但我不知道是哪种类型。换句话说,我可以声明列表是这样的一个类型,但是我不能向它添加元素,我不能100%地确定它是? super Foo类型。Quux可以是那种类型,但也可能不是那种类型。

因此,将List指定为List不允许堆污染,最终也不是问题。

进一步阅读:Angelikalanger一般性解释的相关章节