2017-10-20 3 views
5

今日、私は、オブジェクトが正しいタイプにキャストできない場合でも、Mapにあるオブジェクトをputにできることを発見しました。オブジェクトは、初期化されたマップに一貫性のない型を置くことが予想され、合法ですか?

まず、私は簡単な例から始めてみましょう:あなたはないが既に構築地図に一貫性のない型のオブジェクトを置くことができるはずと

Map<Integer, String> myMap = new HashMap<>(); //plain old hashmap 
myMap.put(9,"star"); //no problem 

myMap.put(10, 1.2); //Incompatible type, the compiler yells 
Map<Integer, Double> aMap = (Map<Integer, Double>) myMap; //Cannot cast, the compiler yells 

はこれまでのところ、すべてが、期待されています。今度は、このことを考えてみましょう:

public class NoRulesForMe { 

    static Object theRing; 

    public static void main(String[] args){ 

     Map<Integer, String> myMap = new HashMap<>(); 
     myMap.put(9,"star"); 

     Map<Integer, Double> myMapMorphed = castWildly(myMap); 
     myMapMorphed.put(99, 3.14); 

     System.out.println(myMapMorphed.get(9)); //"star", as we put in 
     System.out.println(myMapMorphed.get(99)); //3.14, as we put in 
    } 

    public static <T> T castWildly(Object value){ 
     theRing = value; 
     T morphed = (T) theRing; 
     return morphed; 
    } 
} 

私は、これは、実行時エラーが発生しなかったことを驚いている - どのように地図を達成しないと、この動作はJLSやAPIに指定されているので、に依存することができます?

理由私は、より生産性の高いコードでこのことを知っていることを伺いたいと思いますが、これは混乱しているかもしれませんが、機能的に作業することが保証できます。どんな入力も感謝します。

+0

あなたはgoogleの "タイプ消去"する必要があります。 –

+0

これは、Javaがジェネリックを実装していることに固有です。 –

+0

@LouisWasserman Javaのジェネリックスには*何が付いているのですか?私はコンパイラが不平を言っていないのは驚きではない。しかし、もっと大きな疑問は、Javaが 'Double'を' String'としてどのように格納できるかということです。 – flow2k

答えて

1

このタイプのコーディングは非常に危険です!コンパイルはコンパイルされますが、コンパイラは警告を表示します。

注:NoRulesForMe.javaは未チェックまたは安全でない操作を使用します。
注:詳細については、-Xlint:uncheckedを指定して再コンパイルしてください。

これらの警告は、特にジェネリックを使用しているため、無視しないでください。あなたは、キャストが安全であり、後でいくつかの問題を引き起こさないことを確実に(コードに論理的に従って)確実にする必要があります。エラーが発見され、実行時ではなくコンパイラ時に選択されるように常にコード化するのが最善です。ここでコンパイラから与えられた警告は、状況が間違っているかもしれないことを伝えています。

あなたは方法castWildlyからObjectとしてごmyMapを渡している、とあなたがキャストされたとき、あなたはObjectからMapにキャストされています。

コンパイラは、コード内のTにタイプターゲットがMap<String, Double>であることを推測できます。したがって、これを推論できます。ただし、鋳造の際には、Object value(またはObject theRing)のタイプ(サブタイプ)に関する情報はありません。したがって、キャストが安全であることをチェックする方法はありません(特にタイプセーフです)。

このコードの問題は、マップから値を取得するときに発生します。次のコードには追加された行が1つ追加されており、コードはコンパイルされます(上記の警告と同じです)。これは、コンパイラが型チェックを実行するときにMap<String, Double>として宣言されたマップのDoubleの値を取得すると絶対に有効ですが、実行時にコードがクラッシュする(以下に示すランタイムクラッシュエラー)ためです。これは、特にプロダクションコードでは、非常に危険な方法です。あなたがコンパイルして生きているときにあなたの製品がクラッシュする生産コードを配備するよりも、あなたのコンパイラにエラーを与えることをお勧めします。上記のコードを実行している

public class NoRulesForMe { 

    static Object theRing; 

    public static void main(String[] args){ 

     Map<Integer, String> myMap = new HashMap<>(); 
     myMap.put(9,"star"); 

     Map<Integer, Double> myMapMorphed = castWildly(myMap); 
     myMapMorphed.put(99, 3.14); 

     System.out.println(myMapMorphed.get(9)); //"star", as we put in 
     System.out.println(myMapMorphed.get(99)); //3.14, as we put in 

     // added to show why this style of coding causes problems 
     Double testValue1 = myMapMorphed.get(9); 
    } 

    public static <T> T castWildly(Object value){ 
     theRing = value; 
     T morphed = (T) theRing; 
     return morphed; 
    } 
} 

実行時エラー:


3。スレッド14
例外 "メイン" にjava.lang.ClassCastException:さらなる情報についてはjava.lang.StringではNoRulesForMe.mainでjava.lang.Doubleの にキャストすることができない(NoRulesForMe.java:19)

Joshua Bloch著、Effective Javaを読んでください。項目24:未確認の警告を取り除く。 (この項目はGenericsという見出しの下にあります)。

関連する問題