【发布时间】:2015-06-11 11:39:23
【问题描述】:
给定以下两个类定义:
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<?>>。关于@声明的问题不过,下面的 987654334@ 仍然是一个悬而未决的问题。)
但是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 界限的参数,即任何地方Y 和 Z 的参数化不完全相同。
我哪里错了?我对亚型关系的理解不够吗?
(编辑:问题的以下部分仍未得到解答,但我已将其移至新问题here,因为它确实是一个完全不同的问题......抱歉提出乱七八糟,没有很好地使用该网站哈哈...)
除此之外,我在类似情况下也遇到了一些捕获转换问题。采用以下类型声明:
C1<? extends C2<?>> c;
与开头的类似声明 a 不同,它在 JDK-8u45 中编译得很好。但是,如果我们检查specification for capture conversion,看来这个声明应该这次会导致编译时错误。
特别是,新类型变量捕获CAP#T 的上限由glb(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>都是类类型,不是接口类型,但它们都不是另一个的子类型.
definition of the intersection type 本身更清楚地说明了这种(明显的)违规行为。
我确定这不是一个错误,我只是在某个地方犯了一些简单的错误......但如果这里没有人可以为我解释这一点,我会尝试编译器开发邮件列表或其他东西。
感谢您的帮助!
【问题讨论】:
-
这是一个有趣的问题。我只是好奇,这是你有一些理论问题还是你有一些现实世界的课程?我无法想象在哪里使用
class C1<T extends C1<T>> {}或class C2<U> extends C1<C2<U>> {}。 -
有效问题!我没有针对这个特定问题的具体实际应用...但是我确实需要了解正确的预期行为,因为我正在类型系统上编写一堆反射实用程序并想要该行为匹配规范/JDK。目前它大部分都在工作,包括泛型方法调用的类型推断,但是有几个边缘情况(比如这个)会导致问题。如果你想看看它托管在这里:github.com/StrangeSkies/uk.co.strangeskies。还没有 wiki,但
.reflection包的 javadocs 都存在。 -
这实际上与捕获转换关系不大。相反,您应该在 4.10.2 和 4.5.1 中寻找答案。只要您认为这与捕获转化有关,您就会被误导。例如,你为什么
C1<? extends C2<?>>不应该编译的理由是无稽之谈。 (我觉得也是错的,如果被应用了捕获转换我不认为有交集类型,因为capture conversion would not be applied recursively但是我刚醒来所以我有点昏昏沉沉。)跨度> -
我知道第一部分与捕获转换无关,但
c的声明却可以。而且我没有递归地应用它,我只将它应用到C<T1,...,Tn>以提供C<X1,...,Xn>,如第二节所述。 4.5.因为类型参数是一个有上限的通配符,并且类型参数本身也有一个上限,所以得到的上限是这两个边界的交集类型:“如果Ti是@ 形式的通配符类型参数987654369@,那么Si是一个新类型变量,其上限为glb(Bi, Ui[A1:=S1,...,An:=Sn]),下限为空类型。” -
有趣 - IntelliJ 14.1.3 没有显示错误,而
javac显示。创建了一个IntelliJ bug report。
标签: java generics compiler-errors java-8 wildcard