繁体   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;
    // }
}

在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 因此, HasIdAlert的超类型,而不是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.

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