8
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 行有 rawtype 警告,因为必须对 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 )

4

3 回答 3

2

问题是是否HasId<String>是原始类型的超类型Alert。规范在这个问题上不是很清楚。

本着[4.8]的精神,原始类型的超类型也都应该是擦除类型。所以Alert应该有一个超类型HasId,但没有HasId<String>。但是,该部分仅讨论“超类/接口”,而不是“超类型”。

本着 [4.10] 的精神,超类型是通过直接超类型发现的。目前尚不清楚该部分如何应用于原始类型。它可能打算规定 rawAlert有一个直接的超类型HasStringId。这似乎很公平。然后因为HasId<String>是 的直接超类型HasStringId,通过传递性,HasId<String>是 的超类型Alert

混淆的根源在于实际上有两种HasStringId类型,一种是普通的,一种是原始的。尽管HasStringId它本身不是通用的,但它有一个通用的超类型,所以谈论原始版本的HasStringId.

规范没有区分 normal 和 raw HasStringId。这是一个疏忽。

假设我们将原始表示HasStringIdHasStringId',那么 [4.10] 现在更有意义。raw 的直接超级接口Alert是 raw HasStringId'。raw 的直接超级接口HasStringId'是 raw HasId。因此HasId是 的超类型Alert,不是HasId<String>

请参阅JLS 的第 4 节。我在这里链接到上一个 JLS,因为 JLS 7 在第 4.10.2 节中有严重的编辑错误

于 2013-03-11T21:19:30.160 回答
1

我相信这与在没有实际类型参数的情况下如何处理擦除有关。当一个参数化类型在没有任何类型参数的情况下被引用时,对这些参数的所有引用都将被删除。

在这种情况下,您使用了一个Alert没有任何类型参数的参数化类型。这将删除所有类型参数Alert及其超类。HasId这会导致extends-clause of中的type参数HasStringId被删除。Alertthen 不会子类化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;
    }
}
于 2013-03-11T21:49:04.347 回答
1

对于各种泛型细微差别,有许多文档证明 Java 7 编译器比 Java 6 编译器更严格这些情况通常与变得更加具体的实际语言规范有关。该错误可能与使用任何原始类型有关,本质上是“选择退出”继承类型上的泛型 - 但它是否正确仍有待商榷。

编辑:我在JDK 7 incompatibilities 列表中找不到这个问题。使用 sun-jdk-1.7.0_10可以重现该错误,但不能使用 Eclipse 编译器(在泛型细微差别方面,它历来比 javac 有更好的跟踪记录)。您应该提交一个 Oracle 错误

这是一个可能的解决方法:

class AlertController extends BaseController<Alert<?>> {
    @Override
    @SuppressWarnings("unchecked")
    Class<Alert<?>> getModelClass() {
        return (Class<Alert<?>>)(Class<?>)Alert.class;
    }
}
于 2013-03-11T15:03:56.707 回答