Java 规范在哪里说 List<T> 分配给 List<?超级T>?

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

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

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

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

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

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

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

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

List<? super B>§4.10.2. Subtyping among Class and Interface Types:

定义为 List<A> 的超类型

The direct supertypes of the parameterized type C<T<sub>1</sub>,...,T<sub>n</sub>>, where T<sub>i</sub> (1 ≤ i ≤ n) is a type, are all of the following:

  • D<U<sub>1</sub> θ,...,U<sub>k</sub> θ>, where D<U<sub>1</sub>,...,U<sub>k</sub>> is a direct supertype of C<T<sub>1</sub>,...,T<sub>n</sub>> and θ is the substitution [F<sub>1</sub>:=T<sub>1</sub>,...,F<sub>n</sub>:=T<sub>n</sub>].

  • C<S<sub>1</sub>,...,S<sub>n</sub>>, where S<sub>i</sub> contains T<sub>i</sub> (1 ≤ i ≤ n) (§4.5.1).

C<T<sub>1</sub>,...,T<sub>n</sub>> = 列表<A>C<S<sub>1</sub>,...,S<sub>n</sub>> = 列表<?超级 B>。 根据第二条,如果 ? super B 包含 A.

List<? super B>List<A> 的超类型

包含 关系定义在§4.5.1. Type Arguments and Wildcards:

A type argument T<sub>1</sub> is said to contain another type argument T<sub>2</sub>, written T<sub>2</sub> <= T<sub>1</sub>, if the set of types denoted by T<sub>2</sub> is provably a subset of the set of types denoted by T<sub>1</sub> 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.

将列表分配给 < 是什么意思? 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是怎么说的

From the 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 <= ?超级 S 如果 S <: T.


...但这意味着什么?

如果我无法添加 new Quux()new Object()List<? super Foo> 表示此列表仅包含 Foo 的严格超类型的元素,但 我不知道恰好是哪种类型。换句话说,我可以将列表声明为这种类型,但我不能向其中添加元素我不是 100% 确定 是类型 ? super FooQuux可能是那种类型,但也可能不是那种类型。

因此,将 List<Foo> 分配给 List<? super Bar> 不允许堆污染,最终不是问题。

进一步阅读:Relevant section of AngelikaLanger's generic explanation