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

时间:2015-04-24 20:39:26

标签: java generics bounded-wildcard subtyping

假设班级B继承自班级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,因此规范的子类型定义似乎分解为超级通配符。关于“类和接口类型之间的子类型”的第4.10.2节确实提到了通配符,但它只处理通配符出现在潜在子类型中的另一个方向(此方向适合计算的直接超类型机制)。

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

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

2 个答案:

答案 0 :(得分:10)

List<? super B>§4.10.2. Subtyping among Class and Interface Types定义为List<A>的超类型:

  

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

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

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

  •   

允许C<T1,...,Tn> = List<A>C<S1,...,Sn> = List<? super B>。 根据第二个项目符号,如果List<? super B> 包含 List<A>,则? super BA的超类型。

包含关系在§4.5.1

中定义
  

如果由T1表示的类型集合,则类型参数T2被称为包含另一个类型参数T2 <= T1,写为T2可证明是T1表示的类型集的子集,在以下规则的反身和传递闭包下(其中<:表示子类型(§4.5.1. Type Arguments and Wildcards)):

     
      
  • ? 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

答案 1 :(得分:4)

将列表分配给&lt;?超级B&gt;实际上是什么意思?

考虑以下计划:

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:

  

类型参数T1被认为包含另一个类型参数T2,写为T2&lt; = T1,如果由T2表示的类型集合可证明是由T1表示的类型集合的子集,在反射和传递闭包下以下规则(其中&lt;:表示子类型(§4.10)):

     
      
  • ?超级T&lt; =?超S如果S&lt;:T
  •   
  • T&lt; =?超级T
  •   

因此,T&lt; =?如果S&lt;:T

,则为超级S.

......但这意味着什么?

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

因此,将Quux指定为List<Foo>不允许堆污染,最终不会出现问题。

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