2017-10-20 36 views
5

今天,我發現一個能夠put的對象在一個現有的Map中即使對象不能被轉換爲正確的類型。在初始化的Map中放置不一致類型的對象 - 預期的和合法的?

首先,讓我先用一個簡單的例子:

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

你需要谷歌「鍵入擦除」。 –

+0

這在Java的泛型實現中是_inherent_。 –

+0

@LouisWasserman您可否詳細說明* Java的泛型中固有的*是什麼?編譯器沒有抱怨,我並不感到驚訝。但是我認爲更大的問題是Java如何允許「Double」可以存儲爲「String」。 – flow2k

回答

1

這種類型的編碼風險很大!雖然它會編譯,但您會注意到編譯器會發出警告:

注意:NoRulesForMe.java使用未經檢查或不安全的操作。
注意:使用-Xlint重新編譯:取消選中以獲取詳細信息。

這些警告,特別是因爲您使用的是泛型,不應該被忽略,也不應該被壓制。您必須確保(邏輯上遵循代碼)演員安全,並且不會在稍後導致某些問題。最好始終以這樣的方式進行編碼,即在編譯器時間而不是運行時間發現並拾取錯誤。編譯器給出的警告告訴你事情可能會出錯。

你是通過你的myMapObject的方法castWildly,當你是鑄造你是從Object鑄造到Map

編譯器可以推斷出代碼中的T的類型目標爲Map<String, Double>,因此可以推斷出這一點。但是,在投射時,它沒有關於Object value(或Object theRing)是什麼(子)類型的信息。所以它無法檢查演員是否安全(特別是類型安全)。

當您從地圖中檢索值時,此代碼的問題就出現了。下面的代碼有一個額外的添加行,並且編譯代碼(與上面相同的警告)。這是因爲,在編譯器在運行時執行類型檢查時,聲明爲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:消除未檢查的警告。 (這個項目是在泛型標題下)。