14

次のコードを指定します。

public static void main(String[] args) {
        HashMap<String, String> hashMap = new HashMap<>();
        HashMap<String, Object> dataMap = new HashMap<>();
        dataMap.put("longvalue", 5L);

        class TestMethodHolder {
            <T> T getValue(Map<String, Object> dataMap, String value) {
                return (T)dataMap.get(value);
            }
        }

        hashMap.put("test", new TestMethodHolder().<String>getValue(dataMap, "longvalue"));
        String value = hashMap.get("test"); // ClassCastException occurs HERE
        System.out.println(value);
    }

このコードがコンパイルされることは驚くべきことではありませんが、ClassCastException がその上の put 行ではなく get 行で発生することは驚くべきことではありません。ジェネリック型は実行時に消去されるため、getValue() でのキャストは実際には実行時に発生せず、事実上 Object へのキャストです。メソッドが以下のように実装される場合、ランタイム キャストが発生し、put 行で失敗します (予想どおり)。誰でもこれを確認できますか?

class TestMethodHolder {
        String getValue(Map<String, Object> dataMap, String value) {
            return (String)dataMap.get(value);
        }
    }

これは、ジェネリックを使用することの既知の欠陥または奇妙ですか? メソッドを呼び出すときに <> 表記を使用するのは悪い習慣ですか?

編集: デフォルトの Oracle JDK 1.7_03 を使用しています。

上記からのもう 1 つの暗黙の質問: 元の getValue のキャストは実行時にまだ発生していますが、キャストは実際には Object に行われていますか? またはコンパイラは、実行時にこのキャストがまったく発生しないように削除するのに十分スマートですか? これは、ClassCastException が発生している場所の違いを説明している可能性があります。

4

3 に答える 3

12

ライン

return (T)dataMap.get(value);

Unchecked キャスト警告を生成し、仕様に従って、そのような警告が存在すると、コードは型安全ではなくなります。タイプセーフでない結果を間違ったタイプのClassCastException変数に最初に代入しようとすると、コンパイルされたコードにタイプチェックが行われるのはこれが初めてになるため、これが発生します。

Eclipse のコンパイラは、JLS で義務付けられているよりも多くの型チェックを挿入することに注意してください。そのため、Eclipse 内でコンパイルすると、hashMap.put呼び出しは で失敗しCCEます。コンパイラは、この呼び出しには 2 つのString引数が必要であることを認識しているため、実際のメソッド呼び出しの前に型チェックを挿入できます。

ご想像のとおり、ジェネリックTをスペシフィックStringに置き換えると、その時点で型チェックが行われ、失敗します。

于 2013-05-17T14:19:03.797 に答える
0

この型情報はコンパイル中に消去されます ( Neal Gafter の「Reified Generics for Java」を参照)。

実際には、Collectionsユーティリティ メソッドを使用してコレクションを保護できます。

Class<String> type = String.class;
Map<String, String> hashMap = new HashMap<>();
Map<String, String> map = Collections.checkedMap(hashMap, type, type);

Map rawType = map; // pre-Java 1.5 code knows nothing about generics
rawType.put(1, 2); // throws ClassCastException at runtime
于 2013-05-17T15:53:28.130 に答える