java - Java 中的参数化良构和捕获转换

标签 java generics compiler-errors java-8 wildcard

给定以下两个类定义:

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

(编辑:我在这里是一个 dingus,这部分已经回答:C2<?> 扩展 C1<C2<?>> 。下面关于 c 声明的问题仍然是不过,开放性问题。)

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

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

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<?, ?> 并且因此可以接受违反 X 边界的参数,即 YZ 的参数化不是 67915 的任何参数相同。

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

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

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

C1<? extends C2<?>> c;

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

特别是,新类型变量捕获 CAP#T 的上限由 glb(Bi, Ui[A1:=S1,...,An:=Sn]) 给出,在本例中,Bi 解析为通配符绑定(bind) C2<?>,而 Ui[A1:=S1,...,An:=Sn] 解析为 C1<CAP#T>

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

这种(明显的)规则违规在 definition of the intersection type 本身中变得更加清楚。

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

感谢您的帮助!

最佳答案

同时
C2<x> extends C1<C2<x>>对于任何引用类型 x ,
事实并非如此
C2<?> extends C1<C2<?>>

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

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

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

{?}的含义就是声明一个联合类型

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>等。很容易理解它们的含义 - 只需替换 TStringList 的源代码中,我们得到了一个很好的老式普通类。

    interface List<String>
        String get(int)

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

    interface List<?>
        ? get(int)

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

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

表达式的类型是List{?} ,我们知道它是一个对象,而且这个对象一定属于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) ;它返回 x , 和 xObject 的子类型;所以我们可以将返回值分配给 Object .

关于java - Java 中的参数化良构和捕获转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30779745/

相关文章:

java - 解决 JBoss 中的 session 固定问题

c++ - 模板类类型仅适用于基本类型,不适用于类类型

java - JDK 7 中的类型推断比 JDK 6 更严格?

ios - 'NSInvalidArgumentException',添加导航栏后发送到实例的无法识别的选择器

java - 将字符串变量拆分为指定长度的随机子字符串

java - 泛型 Java 1.7 中的钻石 - 如何在 1.6 中为 Java 编译器编写此代码

java - 序言中不能有内容

java - Mockito.any() 通过泛型接口(interface)

java - 将 main() 添加到 Java 类时出现问题

python - cython编译错误: multiple definition of functions