Java 中的参数化格式良好和捕获转换

Parameterization Well Formedness and Capture Conversion in Java

给定以下两个 class 定义:

class C1<T extends C1<T>> {}

class C2<U> extends C1<C2<U>> {}

以及以下类型声明:

C1<C2<?>> a;

直觉上感觉声明的类型 a 应该是有效的,但这不是 JDK-8u45 的行为方式。相反,我们得到类似以下输出的内容:

Test.java:3: error: type argument C2<?> is not within bounds of type-variable T
        C1<C2<?>> a;
             ^
  where T is a type-variable:
    T extends C1<T> declared in class C1
1 error

(编辑:我在这里是个笨蛋,这部分已经回答了:C2<?> 扩展C1<C2<?>>。下面关于 c 声明的问题仍然是一个悬而未决的问题。)

但是 C2<?> 确实 扩展了 C1<C2<?>>,这似乎很容易满足界限。据我所知,对 JLS 的检查没有提供进一步的启发。它真的应该像满足子类型关系的约束一样简单,因为 C2<?> 不是通配符类型,因此捕获转换只是参数的身份转换。

有些情况会变得不太清楚,例如,采用以下class定义:

class C3<T extends C3<?>> {}

class C4<Y, Z> extends C3<C4<Z, Y>> {}

class C5<X extends C3<X>> {
    void accept(X x);
}

所有这些都很好,但是如果我们尝试以下声明:

C5<C6<?, ?>> b;

事情变得陌生了。 C6<?, ?>C3<C6<?, ?>> 的子类型,因此根据我对上面给出的关于声明 C1<C2<?>> 的规范的解释,声明应该是有效的。问题是显然并不是 C6<?, ?> 的每个可能的子类型实际上都满足该界限,所以现在例如 C5.accept() 将其参数类型解析为 C6<?, ?> 并且因此可以接受违反 C6<?, ?> 界限的参数=30=],即 YZ 的参数化不相同的任何地方。

我哪里错了?是不是我对子类型关系的理解不够?

(编辑: 问题的以下部分仍未得到解答,但我已将其移至新问题 因为它确实是一个完全不同的问题...很抱歉弄得一团糟,没有很好地使用该网站哈哈...)

除此之外,我在类似情况下的捕获转换也遇到了一些问题。采用以下类型声明:

C1<? extends C2<?>> c;

与开头的类似声明 a 不同,这在 JDK-8u45 中编译良好。但是,如果我们检查 specification for capture conversion,这次声明 应该 会导致编译时错误。

特别是,新类型变量捕获的上限 CAP#Tglb(Bi, Ui[A1:=S1,...,An:=Sn]) 给出,在这种情况下 Bi 解析为通配符边界 C2<?> Ui[A1:=S1,...,An:=Sn] 解析为 C1<CAP#T>.

由此,glb(C2<?>, C1<CAP#T>)解析为交集类型C2<?> & C1<CAP#T>,这是无效的,因为C2<?>C1<CAP#T>都是class类型,不是接口类型,但它们都不是另一个的子类型。

这种(明显的)违规行为在 definition of the intersection type 本身中更加明显。

我确定这不是一个错误,我只是在某个地方犯了一些简单的错误...但是如果这里没有人可以为我阐明这一点,我会尝试编译器开发邮件列表或其他东西.

感谢您的帮助!

同时
C2<x> extends C1<C2<x>> 对于任何引用类型 x

并非如此 C2<?> extends C1<C2<?>>

通配符 ? 不是类型。这是一个 类型的参数 。虽然语法非常具有欺骗性(设计使然)。

让我们使用不同的语法 - 如果有任何一级通配符,请使用 {} 而不是 <>,例如

List{?},  Map{String, ? extends Number}

{?}的意思是声明一个union类型

List{? extends Number}  ==  union of List<Number>, List<Integer>, List<Long>, ....

不难看出,List<Integer>List{? extends Number}的子类型;
List{? extends Number}List{? extends Object}

的子类型

但是,Foo{?} 不可能是 Foo<x> 的子类型。


在我们的语法中,<> 保留用于用 types 替换类型变量。所以我们写
List<String>, C2<Integer>,等等。很容易理解它们的意思——只要把List源码中的T换成String,就可以得到一个好老的普通class.

    interface List<String>
        String get(int)

这不能用于通配符 - 它没有意义

    interface List<?>
        ? get(int)

所以不允许new ArrayList{?}(),或者class MyList implements List{?}

那么,我们如何使用List{?}呢?我们可以调用哪些方法?

当一个表达式的类型是一个List{?}时,我们知道它是一个对象,而这个对象一定属于一个子class List<x> 的一些未知 类型 x。这是通配符捕获

obj is a List{?}  =>  obj is a List<x>, where x a subtype of Object.

即使 x 的确切类型在编译时未知,我们仍然可以进行替换

    interface List<x>
        x get(int)

所以我们可以理解调用 obj.get(0);它 returns x,并且 xObject 的子类型;所以我们可以将 return 值分配给 Object.