简体   繁体   English

为什么这些泛型不在OpenJDK7中编译,而在OpenJDK6中编译

[英]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;
    // }
}

compiles fine on OpenJDK6, but in OpenJDK7 gives: 在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

Note that there's rawtype warning at line 50, because Alert must be parameterized. 请注意,在第50行有原始类型警告,因为必须将Alert参数化。 If I do that, eg extends BaseController<Alert<Object>> , code compiles. 如果我这样做,例如extends BaseController<Alert<Object>> ,代码就会编译。 But I cannot do that, because I need to implement getModelClass(). 但是我不能这样做,因为我需要实现getModelClass()。

UPDATE: That was a bug in Java 6 implementations, which was fixed in Java 7: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6559182 . 更新:这是Java 6实现中的错误,已在Java 7中修复: http : //bugs.sun.com/bugdatabase/view_bug.do?bug_id=6559182 (And here's my question to compiler devs: 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 ) (这是我对编译器开发人员的问题: 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

The question is whether HasId<String> is a supertype of the raw type Alert . 问题是HasId<String>是否是原始类型Alert的超类型。 The spec is not very clear on this issue. 规格在这个问题上不是很清楚。

In the spirit of [4.8], the supertypes of a raw type should all be erased types too. 本着 [4.8]的精神 ,原始类型的超类型也应全部删除。 So Alert should have a supertype HasId , but not HasId<String> . 因此Alert应该具有HasId的超类型,但不能具有HasId<String> However the section talks only in terms of "super classes/interfaces", not in terms of "supertypes". 但是,本节仅针对“超类/接口”进行讨论,而不针对“超类型”进行讨论。

In the spirit of [4.10], the supertypes are discovered through direct supertypes. 本着[4.10]的精神,通过直接超型发现超型。 It's unclear how the section applies to raw types. 目前尚不清楚本节如何适用于原始类型。 It probably intends to rule that raw Alert has a direct supertype HasStringId . 它可能打算裁定原始Alert具有直接超类型HasStringId That seems fair. 看起来很公平。 Then because HasId<String> is a direct supertype of HasStringId , by transitivity, HasId<String> is a supertype of Alert ! 然后,由于HasId<String>HasId<String>的直接超类型, HasStringId通过传递性, HasId<String>Alert的超类型!

The confusion is rooted in the fact that there are actually two HasStringId types, one normal, one raw. 混淆的根源在于实际上有两种HasStringId类型,一种是普通的,一种是原始的。 Even though HasStringId is not generic in itself, its has a generic supertype, so it makes sense to talk about the raw version of HasStringId . 即使HasStringId本身不是通用的,它也具有通用的超类型,因此谈论HasStringId的原始版本是HasStringId

The spec does not make a distinction between the normal and raw HasStringId . 规范没有在普通HasStringId和原始HasStringId之间进行区分。 That's an oversight. 这是一个疏忽。

Suppose we denote the raw HasStringId as HasStringId' , then [4.10] makes more sense now. 假设我们将原始的HasStringId表示为HasStringId' ,那么[4.10]现在更有意义。 The direct super interface of raw Alert is raw HasStringId' . raw Alert的直接超级接口是raw HasStringId' The direct super interface of raw HasStringId' is raw HasId . Raw HasStringId'的直接超级接口是raw HasId Therefore HasId is a supertype of Alert , not HasId<String> . 因此, HasIdAlert的超类型,而不是HasId<String>

See section 4 of JLS . 参见JLS的第4节 I'm linking to the prev JLS here, since JLS 7 has serious editing errors in section 4.10.2 我在这里链接到上一个JLS,因为JLS 7在4.10.2节中存在严重的编辑错误

There are a number of documented cases of Java 7 compilers being stricter than Java 6 compilers for various generics nuances. 在许多通用的细微差别中, 有许多 Java 7编译器的案例比Java 6编译器更为严格。 These cases are often related to the actual language spec having become more specific. 这些情况通常与实际语言规范变得更加具体有关。 The error likely has to do with the use of any raw type essentially "opting out" of generics on inherited types - whether it's correct is debatable though. 该错误可能与使用任何原始类型实质上在继承的类型上“选择退出”泛型有关—尽管是否正确尚有待商bat。

EDIT: I couldn't find this issue in the list of JDK 7 incompatibilities . 编辑:我在JDK 7不兼容列表中找不到此问题。 The error is reproducible using sun-jdk-1.7.0_10 , but not with the Eclipse compiler (which historically has a much better track record than javac when it comes to generics nuances). 使用sun-jdk-1.7.0_10可以重现该错误,但使用Eclipse编译器则无法重现(就泛型细微差别而言 ,Eclipse编译器的记录远优于javac)。 You should submit an Oracle bug . 您应该提交一个Oracle错误

Here's a possible workaround: 这是一个可能的解决方法:

class AlertController extends BaseController<Alert<?>> {
    @Override
    @SuppressWarnings("unchecked")
    Class<Alert<?>> getModelClass() {
        return (Class<Alert<?>>)(Class<?>)Alert.class;
    }
}

I believe this has to do with how erasure is handled in the absence of actual type parameters. 我认为这与在没有实际类型参数的情况下如何处理擦除有关。 When a parameterized type is referenced without any type parameters, all references to those parameters are erased. 当引用没有任何类型参数的参数化类型时,将删除对这些参数的所有引用。

In this instance, you have a parameterized type Alert being used without any type parameter. 在这种情况下,将使用没有任何类型参数的参数化类型Alert This erases all type parameters on Alert and its superclasses. 这将删除Alert及其超类上的所有类型参数。 This causes the type parameter of HasId in the extends-clause of HasStringId to be erased. 这将导致HasId的扩展子句中的HasStringId的类型参数被擦除。 Alert then does not subclass HasId<String> because HasStringId no longer extends it but rather extends HasId . 然后Alert不会将HasId<String>子类HasId<String>因为HasStringId不再扩展它,而是扩展了HasId

Paul B.'s workaround or the one below avoids this issue by always using Alert with its type parameters. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM