[英]Why these generics don't compile in OpenJDK7, but do in OpenJDK6
class HasId<I> {}
class HasStringId extends HasId<String> {}
class Alert<T extends /*Some*/Object> extends HasStringId {}
class BaseController<M extends HasId<String>> {
// abstract Class<M> getModelClass();
}
class AlertController extends BaseController<Alert> { // error here
// @Override Class<Alert> getModelClass() {
// return Alert.class;
// }
}
在OpenJDK6上可以正常编译,但在OpenJDK7中可以提供:
AlertController.java:50: error: type argument Alert is not within bounds of
type-variable T
class AlertController extends BaseController<Alert> {
^
where T is a type-variable:
T extends HasId<String> declared in class BaseController
请注意,在第50行有原始类型警告,因为必须将Alert参数化。 如果我这样做,例如extends BaseController<Alert<Object>>
,代码就会编译。 但是我不能这样做,因为我需要实现getModelClass()。
更新:这是Java 6实现中的错误,已在Java 7中修复: http : //bugs.sun.com/bugdatabase/view_bug.do?bug_id=6559182 。 (这是我对编译器开发人员的问题: http : //openjdk.5641.n7.nabble.com/Nested-generics-don-t-compile-in-1-7-0-15-but-do-in-1 -6-0-27-td121820.html )
问题是HasId<String>
是否是原始类型Alert
的超类型。 规格在这个问题上不是很清楚。
本着 [4.8]的精神 ,原始类型的超类型也应全部删除。 因此Alert
应该具有HasId
的超类型,但不能具有HasId<String>
。 但是,本节仅针对“超类/接口”进行讨论,而不针对“超类型”进行讨论。
本着[4.10]的精神,通过直接超型发现超型。 目前尚不清楚本节如何适用于原始类型。 它可能打算裁定原始Alert
具有直接超类型HasStringId
。 看起来很公平。 然后,由于HasId<String>
是HasId<String>
的直接超类型, HasStringId
通过传递性, HasId<String>
是Alert
的超类型!
混淆的根源在于实际上有两种HasStringId
类型,一种是普通的,一种是原始的。 即使HasStringId
本身不是通用的,它也具有通用的超类型,因此谈论HasStringId
的原始版本是HasStringId
。
规范没有在普通HasStringId
和原始HasStringId
之间进行区分。 这是一个疏忽。
假设我们将原始的HasStringId
表示为HasStringId'
,那么[4.10]现在更有意义。 raw Alert
的直接超级接口是raw HasStringId'
。 Raw HasStringId'
的直接超级接口是raw HasId
。 因此, HasId
是Alert
的超类型,而不是HasId<String>
。
参见JLS的第4节 。 我在这里链接到上一个JLS,因为JLS 7在4.10.2节中存在严重的编辑错误
在许多通用的细微差别中, 有许多 Java 7编译器的案例比Java 6编译器更为严格。 这些情况通常与实际语言规范变得更加具体有关。 该错误可能与使用任何原始类型实质上在继承的类型上“选择退出”泛型有关—尽管是否正确尚有待商bat。
编辑:我在JDK 7不兼容列表中找不到此问题。 使用sun-jdk-1.7.0_10可以重现该错误,但使用Eclipse编译器则无法重现(就泛型细微差别而言 ,Eclipse编译器的记录远优于javac)。 您应该提交一个Oracle错误 。
这是一个可能的解决方法:
class AlertController extends BaseController<Alert<?>> {
@Override
@SuppressWarnings("unchecked")
Class<Alert<?>> getModelClass() {
return (Class<Alert<?>>)(Class<?>)Alert.class;
}
}
我认为这与在没有实际类型参数的情况下如何处理擦除有关。 当引用没有任何类型参数的参数化类型时,将删除对这些参数的所有引用。
在这种情况下,将使用没有任何类型参数的参数化类型Alert
。 这将删除Alert
及其超类上的所有类型参数。 这将导致HasId
的扩展子句中的HasStringId
的类型参数被擦除。 然后Alert
不会将HasId<String>
子类HasId<String>
因为HasStringId
不再扩展它,而是扩展了HasId
。
Paul B.的变通方法或以下解决方法始终使用Alert
及其类型参数来避免此问题。
class AlertController<T> extends BaseController<Alert<T>> {
@Override Class<Alert<T>> getModelClass() {
return cast(Alert.class);
}
@SuppressWarnings("unchecked")
private <T> T cast(final Object o) {
return (T) o;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.