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

Alert はパラメータ化する必要があるため、行 50 に rawtype 警告があることに注意してください。これを行うと、たとえば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]の精神で、raw 型のスーパータイプもすべて消去型であるべきです。したがってAlert、スーパータイプが必要ですが、そうではありHasIdませんHasId<String>。ただし、このセクションでは、「スーパータイプ」についてではなく、「スーパークラス/インターフェース」についてのみ説明しています。

[4.10] の精神で、スーパータイプは直接のスーパータイプを通じて発見されます。このセクションが生の型にどのように適用されるかは不明です。おそらく、 rawAlertが直接のスーパータイプを持つと判断するつもりHasStringIdです。それは公平に思えます。次に、HasId<String>は の直接のスーパータイプであるためHasStringId、推移性により!HasId<String>のスーパータイプです。Alert

混乱は、実際には 2 つのHasStringIdタイプ (1 つはノーマル、もう 1 つは生) があるという事実に根ざしています。はそれHasStringId自体ではジェネリックではありませんが、ジェネリックなスーパータイプを持っているため、生のバージョンの について話すのは理にかなっていHasStringIdます。

仕様上、通常と未加工の区別はありませんHasStringId。それは見落としです。

raw を と表すと、HasStringId[ HasStringId'4.10] がより理にかなっています。raw のダイレクト スーパー インターフェイスは rawAlertですHasStringId'。raw のダイレクト スーパー インターフェイスは rawHasStringId'ですHasId。したがって、 はではなくHasIdのスーパータイプです。AlertHasId<String>

JLS のセクション 4 を参照してください。JLS 7 にはセクション 4.10.2 に重大な編集エラーがあるため、ここで前の JLS にリンクしています。

于 2013-03-11T21:19:30.160 に答える
1

さまざまなジェネリックのニュアンスについて、Java 7 コンパイラが Java 6 コンパイラよりも厳密であるという文書化されたケースが多数あります。これらのケースは、多くの場合、実際の言語仕様がより具体的になったことに関連しています。エラーは、継承された型のジェネリックを本質的に「オプトアウト」する生の型の使用に関係している可能性があります-それが正しいかどうかは議論の余地があります.

編集: JDK 7 非互換性のリストでこの問題を見つけることができませんでした。このエラーは、 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 に答える
1

これは、実際の型パラメーターがない場合に消去がどのように処理されるかに関係していると思います。パラメーター化された型が型パラメーターなしで参照されると、それらのパラメーターへのすべての参照が消去されます。

この例では、Alert型パラメーターなしでパラメーター化された型が使用されています。Alertこれにより、 とそのスーパークラスのすべての型パラメーターが消去されます。これにより、HasIdextends-clause のtype パラメータHasStringIdが消去されます。then は、それを拡張するのではなく、 を拡張するため、Alertサブクラス化しません。HasId<String>HasStringIdHasId

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 に答える