14

このコードに未チェックのキャスト警告がある理由を理解しようとしています。最初の 2 つのキャストには警告がありませんが、3 番目のキャストには警告があります。

class StringMap<V> extends HashMap<String, V> {
}

class StringToIntegerMap extends HashMap<String, Integer> {
}

Map<?, ?> map1 = new StringToIntegerMap();
if (map1 instanceof StringToIntegerMap) {
    StringToIntegerMap stringMap1 = (StringToIntegerMap)map1; //no unchecked cast warning
}

Map<String, Integer> map2 = new StringMap<>();
if (map2 instanceof StringMap) {
    StringMap<Integer> stringMap2 = (StringMap<Integer>)map2; //no unchecked cast warning
}

Map<?, Integer> map3 = new StringMap<>();
if (map3 instanceof StringMap) {
    StringMap<Integer> stringMap3 = (StringMap<Integer>)map3; //unchecked cast warning
}

stringMap3これは、キャストに対する完全な警告です。

型の安全性: からMap<capture#3-of ?,Integer>への未チェックのキャストStringMap<Integer>

ただし、StringMapクラス宣言では (ie, ) の最初の型パラメーターが指定されてMapおり、 とキャストのString両方で (ie, )の2 番目の型パラメーターに同じ型が使用されます。私が理解していることから、キャストがスローしない限り(チェックがあるのでスローしない限り)、有効になります。map3StringMap<Integer>MapIntegerClassCastExceptioninstanceofstringMap3Map<String, Integer>

これは Java コンパイラの制限ですか? ClassCastExceptionまたは、特定の引数を指定して map3 または stringMap3 のいずれかのメソッドを呼び出すと、警告が無視された場合に予期しないエラーが発生するシナリオはありますか?

4

4 に答える 4

8

動作は仕様どおりです。Java 言語仕様のセクション 5.5.2 では、未チェックのキャストは次のように定義されています。

S型からパラメーター化された型へのキャストTは、次のいずれかが true でない限りチェックされません。

  • S <: T

  • の型引数はTすべて無制限のワイルドカードです

  • T <: Sの型引数が の型引数に含まれていない場合を除き、Sにはサブタイプがありません。XTXT

(A <: B意味: "Aは " のサブタイプですB)。

最初の例では、ターゲット タイプにワイルドカードがありません (したがって、それらはすべて無制限です)。2番目の例でStringMap<Integer>は、実際にはのサブタイプですMap<String, Integer>X3番目の条件で言及されているサブタイプはありません)。

ただし、3 番目の例では、 from からMap<?, Integer>to へのキャストStringMap<Integer>があり、ワイルドカードのため?、どちらも他方のサブタイプではありません。また、明らかに、すべての型パラメーターが無制限のワイルドカードであるとは限らないため、いずれの条件も適用されません。これは未チェックの例外です。

コード内でチェックされていないキャストが発生した場合、適合する Java コンパイラが警告を発行する必要があります。

あなたと同じように、キャストが無効になるシナリオは見当たりません。そのため、これは Java コンパイラの制限であると主張できますが、少なくとも指定された制限です。

于 2015-12-02T22:10:48.523 に答える
3

実際、その答えは Java 言語仕様にあります。 セクション 5.1.10では、ワイルドカードを使用すると、フレッシュ キャプチャ タイプになると述べています。

これは、Map<String, Integer>が のサブクラスではないことを意味します。Map<?, Integer>したがって、 が型に代入可能であっても、が何らかのキー型と Integer 値を持つマップを表しているためStringMap<Integer>、キャストは安全ではありません。これが、チェックされていないキャストの警告が表示される理由です。Map<?, Integer>Map<?, Integer>StringMap<Integer>

割り当てとキャストの間のコンパイラの観点からは、何かが存在する可能性があるためStringMap<Integer>、キャスト操作で map3 が のインスタンスであっても、 map3 はMap<?, Integer>その型であるため、警告は完全に正当です。

そして、あなたの質問に答えるには:はい、 map3 インスタンスがキーとして文字列しか持たない限り、あなたのコードはおそらくそれを保証できません。

理由を見てください:

Map<?, Integer> map3 = new StringMap<Integer>();

// valid operation to use null as a key. Possible NPE depending on the map implementation
// you choose as superclass of the StringMap<V>.
// if you do not cast map3, you can use only null as the key.
map3.put(null, 2); 

// but after an other unchecked cast if I do not want to create an other storage, I can use
// any key type in the same map like it would be a raw type.
((Map<Double, Integer>) map3).put(Double.valueOf(0.3), 2); 

// map3 is still an instance of StringMap
if (map3 instanceof StringMap) {
    StringMap<Integer> stringMap3 = (StringMap<Integer>) map3; // unchecked cast warning
    stringMap3.put("foo", 0);
    for (String s : stringMap3.keySet()){
        System.out.println(s+":"+map3.get(s));
    }
}

結果:

null:2
foo:0
Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String
于 2015-11-30T19:01:34.033 に答える
3

もちろん、Java 言語仕様の観点から観察された動作を説明する答えよりも「正しい」(より正しい? ) 答えを提供することはできません。でも:

  • 実際の用語で与えられた観察された動作の説明は、JLS を投げかけるだけの説明よりも理解しやすく、覚えやすい場合があります。その結果、実際的な説明は、多くの場合、JLS に関する説明よりも有用です。

  • JLS は、実際の制約を満たす必要があるため、まさにその通りであり、他の方法ではありません。言語によって行われた基本的な選択を考えると、多くの場合、JLS の詳細は、それ以外の方法ではあり得ませんでした。これは、特定の動作の実際的な理由がJLS よりも重要であると考えることができることを意味します。なぜなら、それらが JLS を形成したからであり、JLS がそれらを形成しなかったからです。

したがって、何が起こっているかについての実際的な説明は次のとおりです。


以下:

Map<?, ?> map1 = ...;
StringToIntegerMap stringMap1 = (StringToIntegerMap)map1; 

ジェネリック型にキャストていないため、未チェックのキャスト警告は表示されません。これは、次のことを行うのと同じです。

Map map4 = ...; //gives warning "raw use of generic type"; bear with me.
StringToIntegerMap stringMap4 = (StringToIntegerMap)map4; //no unchecked warning!

以下:

Map<String, Integer> map2 = ...;
StringMap<Integer> stringMap2 = (StringMap<Integer>)map2;

左側のジェネリック引数が右側のジェネリック引数と一致するため、チェックされていないキャストの警告は表示されません。(どちらも<String,Integer>)


以下:

Map<?, Integer> map3 = ...;
StringMap<Integer> stringMap3 = (StringMap<Integer>)map3;

<String,Integer>左側は右側<?,Integer>ですが、ワイルドカードを特定のタイプにキャストすると、常にそのような警告が予想されるため、未チェックのキャスト警告が表示されます。(または、より厳密に制限された、より具体的なタイプへの制限されたタイプ。)この場合の「整数」は赤いニシンであることに注意してください。Map<?,?> map3 = new HashMap<>();

于 2015-12-04T16:14:55.163 に答える
3

このキャストは安全ではありません。あなたが持っているとしましょう:

Map<?, Integer> map3 = new HashMap<String,Integer>();
StringMap<Integer> stringMap3 = (StringMap<Integer>)map3;

それは例外をスローします。StringMap<Integer>を新しく作成して map3 に割り当てたことを知っていてもかまいません。あなたがしていることは、ダウンキャストとして知られています詳細については、Javaでのダウンキャスト を参照してください。

編集:また、すべてのジェネリックで問題を複雑にしすぎています。ジェネリック型がなくてもまったく同じ問題が発生します。

于 2015-11-23T00:37:27.660 に答える