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

2022-09-03 07:06:28

假定类继承自类 。以下是合法的 Java:BA

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

就规范而言,这意味着 assignsTo .但是,我很难找到规范中说这是合法的部分。特别是,我认为我们应该有子类型关系List<A>List<? super B>

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

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

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

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


答案 1

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

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

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

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

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

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

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

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

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

  • T <= T

  • T <= ? extends T

  • T <= ? super T

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


答案 2

将列表分配给<?超级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 在以下规则的自反和传递闭包下表示的类型集的子集(其中<:表示子类型 (§4.10)),则类型参数 T1 称为包含另一个类型参数 T2,编写为 T2 <= T1:

  • ?超级 T <= ?如果 S <:T,则为超级 S
  • T <= ?超级T

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


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

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

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

延伸閱讀:AngelikaLanger一般解釋的相關部分


推荐