假设类B继承自类A。以下是合法Java:
List x;
List super B> y = x;
就规范而言,这意味着List转让给List super B>。但是,我很难找到规范中说明这是合法的部分。特别是,我认为我们应该有亚型关系
List <: super b>
号
但是Java 8规范的第4.10节定义了子类型关系作为直接超关系EDCOX1〔4〕的传递闭包,并且它定义了直接超关系,它是用一个有限函数来计算EDOCX1×5的一组超类型。在输入List上没有可以生成List super B>的有界函数,因为可能有任意数量的B继承自A,所以规范的子类型定义似乎对超级通配符是分解的。关于"类和接口类型之间的子类型"的第4.10.2节确实提到了通配符,但它只处理通配符出现在潜在子类型中的另一个方向(该方向适合于计算的直接父类型机制)。
问题:规范的哪一部分说明上述代码是合法的?
编译器代码的动机是,所以不足以理解为什么它是直观合法的,或者想出一个处理它的算法。由于Java中的泛型子类型问题是不可判定的,所以我希望处理与SPEC完全相同的情况,因此需要处理该情况的规范的一部分。
定义"子类型关系"?仿制药是不可复制的,所以我看不出你的观点。
不幸的是,我能做的最好的就是说,子类型关系在规范的第4.10节中定义:()第一个原则定义在这里没有帮助,因为它不本地化规范的哪个部分说我的特定通配符assignsto是有效的。我看不到规范的子类型关系处理它,但是我也看不到规范的assignsto关系如何处理它,所以我丢失了。
为了澄清,我所说的子类型的原因是,assignsto是通过扩大引用转换来定义的,而引用转换是通过子类型来定义的。然而,assignsto也被定义为一堆其他关系,因此相关的位可能与子类型无关。
List super B>由§;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> = List和C1,...,Sn> = List super B>。根据第二颗子弹,如果? super B含有A,List super B>是List的超类型。
包含关系在§;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
? super T <= ? super S if S
T <= T
T <= ? extends T
T <= ? super T
号
通过第二颗子弹,我们可以看到? super B含有? super A。最后一颗子弹,我们看到? super A含有A。因此,我们知道? super B包含A。
我做了这个社区维基,因为我不确定我是否所有的细节都正确。语言律师,请纠正我的任何错误。
"我不确定所有的细节都是正确的。"你做到了。
正如我在问题中提到的,4.10.2说要将捕获转换应用于子类型的对象,以找到它的直接父类型。但是,在这种情况下,子类型是List,因此没有要应用的捕获转换。因此,在我的案例中,我还不理解该部分是如何工作的。
啊,明白了。我错过了关键的一点。谢谢!
@geoffreyirving事实上,我认为你是对的,? super A不会在分配上下文中被捕获,但这并不重要。4.10.2和4.5.1的摘录表明,List super A>是List的直接超型。
太好了,上次的编辑删除了我误入歧途的部分。一切都好!
将列表分配给什么?超级B>实际上是什么意思?
考虑以下程序:
public class Generics {
static class Quux { }
static class Foo extends Quux { }
static class Bar extends Foo { }
public static void main(String... args) {
List 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 ? super T <= ? super S if S T <= ? super T
号
因此,t<=?super s如果s…但这意味着什么?
如果我不能添加一个new Quux(),或者一个new Object()?List super Foo>意味着这个列表只包含对Foo是严格超类型的元素,但我不知道是哪种类型。换句话说,我可以声明列表是这样的一个类型,但是我不能向它添加元素,我不能100%地确定它是? super Foo类型。Quux可以是那种类型,但也可能不是那种类型。
因此,将List指定为List super Bar>不允许堆污染,最终也不是问题。
进一步阅读:Angelikalanger一般性解释的相关章节
这是一个很好的解释,我希望我的问题是关于直觉的理解或者安全的语义证明。两个都会容易得多!:)不幸的是,我陷入了编译器内部,特别需要在规范中为这种情况定义assignsto或subtype的位置。您完全正确地认为它应该与类型参数上的包含关系相关,但不幸的是,我不知道如何处理。