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

时间:2015-06-11 11:39:23

标签: 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边界的参数。 {1}},即YZ的参数化不相同的任何地方。

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

编辑>问题的以下部分仍未得到答复,但我已将其移至新问题here,因为它确实是一个完全不同的问题...抱歉搞得一塌糊涂,没有很好地使用网站哈哈...)

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

C1<? extends C2<?>> c;

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

特别是,新类型变量capture [{1}}的上限由CAP#T给出,在这种情况下glb(Bi, Ui[A1:=S1,...,An:=Sn])解析为通配符绑定Bi和{ {1}}解析为C2<?>

由此,Ui[A1:=S1,...,An:=Sn]解析为交集类型C1<CAP#T>,这是无效的,因为glb(C2<?>, C1<CAP#T>)C2<?> & C1<CAP#T>都是类类型,不是接口类型,但都不是他们是另一个的子类型。

definition of the intersection type本身更明确地说明了(明显的)规则违规行为。

我确定这不是一个错误而且我只是在某个地方犯了一些简单的错误......但是如果这里没有人可以为我解释这一点,我会尝试编译器 - dev邮件列表或其他东西。< / p>

感谢您的帮助!

1 个答案:

答案 0 :(得分:3)

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>的子类型。

在我们的语法中,<>保留用于替换带有类型的类型变量。所以我们写了 List<String>, C2<Integer>等等。很容易理解它们的含义 - 只需在T的源代码中用String替换List,我们就会得到一个古老的普通类。

    interface List<String>
        String get(int)

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

    interface List<?>
        ? get(int)

因此不允许new ArrayList{?}()class MyList implements List{?}

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

表达式的类型List{?}时,我们知道它是一个对象,并且该对象必须属于某个未知的List<x>的子类< strong>类型 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