3

Javaジェネリックシステムのいくつかのコーナーケースでまだ問題があります。

私はこの方法を持っています(私は署名にのみ興味があります):

 interface Extractor<RETURN_TYPE> {
    public <U extends Enum<U>> RETURN_TYPE extractEnum(final Class<U> enumType);
}

(実装が時々 EnumSet を抽出し、実装が JComboBox などを抽出するインターフェースについて考えてみてください。)

実行時に取得したクラスで呼び出したいので、次のように呼び出します。

 public static <RETURN_TYPE> RETURN_TYPE extractField(final Extractor<RETURN_TYPE> extractor, final Field field) {
    final Class<?> type = field.getType();
    if (type.isEnum())
        return extractor.extractEnum(/* error here*/type.asSubclass(Enum.class));
    throw new RuntimeException("the rest of the visitor is not necessary here");
}

奇妙なエラーメッセージが表示されます:互換性のないタイプが見つかりました:java.lang.Object required:RETURN_TYPE

タイプの「t」の前で、呼び出しの開始ブラケットの直後の場合のメッセージの場所。

非ジェネリックなコンテキストから呼び出すと、動作します:

    Integer extractField(final Extractor<Integer> extractor, final Field field) {
        final Class<?> type = field.getType();
        if (type.isEnum())
            return extractor.extractEnum(type.asSubclass(Enum.class));
        throw new RuntimeException("the rest of the visitor is not necessary here");
    }

誰かがこの問題の説明と解決策を持っていますか?

これで遊びたい人のための完全なファイルは次のとおりです。

public class Blah {
    interface Extractor<RETURN_TYPE> {
        public <U extends Enum<U>> RETURN_TYPE extractEnum(final Class<U> enumType);
    }

    public static <RETURN_TYPE> RETURN_TYPE extractField(final Extractor<RETURN_TYPE> extractor, final Field field) {
        final Class<?> type = field.getType();
        if (type.isEnum())
            return extractor.extractEnum(/* error here*/type.asSubclass(Enum.class));
        throw new RuntimeException("the rest of the visitor is not necessary here");
    }

    public static Integer extractField(final Extractor<Integer> extractor, final Field field) {
        final Class<?> type = field.getType();
        if (type.isEnum())
            return extractor.extractEnum(type.asSubclass(Enum.class));
        throw new RuntimeException("the rest of the visitor is not necessary here");
    }
}

前もって感謝します、

ニコ

4

3 に答える 3

3

実際、これがコンパイラのバグであるとしても、私は驚かないでしょう。ジェネリックスの真剣な使用(パラメーター化されたメソッド、制限付きワイルドカード、およびジェネリックスの他の「高度な」使用法の組み合わせ)を通じて、昨年javacで2つまたは3つの問題に遭遇しました(厄介なことに、同じユニット多くの場合、IDEで正常にコンパイルされます)。

あなたの場合、それはバグだと私はかなり確信しています。なぜなら、コンパイラが不平を言っているのは、ではなくextractor.extractEnumを返すということだからです。そして、列挙型メソッドの引数でどのようなクレイジーな推論が行われるかに関係なく、型シグネチャからExtractorがであることがわかっているため、常にと言うことができるはずです。ObjectRETURN_TYPEExtractor<RETURN_TYPE>return extractor.extractEnum(...);

ひどい証拠は、引数を使用してメソッドを呼び出したとしてもnull(したがって、引数の列挙型ジェネリックスから潜在的な複雑さを完全に削除したとしても)、コンパイラーは依然として文句を言うことです。U<RETURN_TYPE>特に、Extractorからのリターンタイプは明らかにゴミだと考えているとのことです。

一般に、これらの問題を回避するための解決策は、いくつかの明示的なキャストを投入することです。extractEnumの出力をRETURN_TYPEにキャストした場合、コンパイラーは満足していますか? 編集:いいえ、それは実際にはそうではありません-それはそれを不平を言い、U<RETURN_TYPE>変換できませんRETURN_TYPE-深い...

最近の1.6コンパイラを使用している場合、これはjavacの非常に大きな問題であるため、Sunに報告することをお勧めします。これを実行する非常に短いテストケースを次に示します。

public class Test {
  interface Sub<O> {
    public <I extends Enum<I>> O method(final Class<I> enumType);
  }

  public static <O> O go(final Sub<O> sub) {
    return sub.method(null);
  }
}

PSジェネリック型パラメーターを指定するために、単一の大文字を使用するのが一般的な規則です。「私は正しい、あなたは間違っている」と言うつもりはありませんが、代わりにExtractorを使用した場合よりも、コードを読み、理解するのがはるかに難しいことを覚えておいてください。(そして、ヘマルの答えの言い回しから判断すると、それは彼にとっても同じです。)

于 2009-10-09T16:39:27.207 に答える
2

元の問題を推測できませんでした。

Extractor.extract2 つの型パラメーターがあり、Uどちらが である必要がありEnumTどちらが任意の型であるかは正しいですか? 一般的な呼び出しでは、とVVの両方ですか? である場合、パラメータは である必要があります。以下は私のためにコンパイルされますが、ご覧のとおり、ジェネリックメソッドは次のインスタンスを提供する必要がありますTUUVVClass<VV>Class<Enum>Class<VV>

class Outer {
  static class Extractor<T> {
    public <U extends Enum<U>> T extract(final Class<U> lala) {
      return null;
    }
    // two type parameters, T and U
    // U must be an enum
    // T is arbitrary class
  }

  static <VV extends Enum<VV>> VV extract(final Extractor<VV> extractor, Class<VV> vvClass) {
    final Class<?> type = null;
    return extractor.extract(vvClass);
    // Outer.extract returns VV 
    // T -> VV
    // it seems VV is also U
  }
}
于 2009-10-09T05:03:56.460 に答える
1

Field.getType( )ジェネリッククラスのみを返すようです。すでに消去された型情報を持つ型を配置しようとするため、この関数は"unchecked"警告を発する必要があり、ジェネリック インターフェイスのすべての型情報が消去されます。

タイプ消去を使用すると、インターフェイスは次のようになります。

interface Extractor {
    public Object extractEnum( final Class enumType );
}

したがって、すべての型情報が消去されるため、の戻り値の型はextractEnumisjava.lang.Objectになるため、特定のキャストを追加する必要があります。そして、これはまさにあなたが持っているエラーメッセージです.

コードの変更例を次に示します。

@SuppressWarnings( "unchecked" )
public static <RETURN_TYPE> RETURN_TYPE extractField(
       final Extractor<RETURN_TYPE> extractor,
       final Field field
    )
{
    final Class type = field.getType(); // unchecked

    if (type.isEnum())
    {
        // needs cast
        return (RETURN_TYPE) extractor.extractEnum( type ); // unchecked
    }
    throw new RuntimeException("the rest of the visitor is not necessary here");
}

このコメントは無視してください: コンパイル エラーが発生した理由に関する元の質問に答えるには。これは、丸い穴のような問題に四角いペグです。type.asSubclass( Enum.class )を返しますが、インターフェイス呼び出しが期待するClass< ? extends Enum >ものとはまだ同じではありません。Class< U >

于 2009-10-09T14:43:28.787 に答える