簡體   English   中英

類型安全:未經檢查的強制轉換

[英]Type safety: Unchecked cast

在我的 spring 應用程序上下文文件中,我有類似的內容:

<util:map id="someMap" map-class="java.util.HashMap" key-type="java.lang.String" value-type="java.lang.String">
    <entry key="some_key" value="some value" />
    <entry key="some_key_2" value="some value" />   
</util:map>

在java類中,實現如下:

private Map<String, String> someMap = new HashMap<String, String>();
someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");

在 Eclipse 中,我看到一條警告說:

類型安全:從 Object 到 HashMap<String,String> 的未經檢查的強制轉換

什么地方出了錯?

問題是強制轉換是運行時檢查 - 但由於類型擦除,在運行時HashMap<Foo,Bar>對於任何其他FooBar HashMap<String,String>HashMap<String,String>HashMap<Foo,Bar>之間實際上沒有區別。

使用@SuppressWarnings("unchecked")並按住鼻子。 哦,還有在 Java 中進行具體化泛型的活動:)

好吧,首先,您在使用新的HashMap創建調用浪費內存。 你的第二行完全忽略了對這個創建的哈希圖的引用,使其可供垃圾收集器使用。 所以,不要這樣做,使用:

private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");

其次,編譯器抱怨您將對象HashMap轉換為HashMap而不檢查它是否為HashMap 但是,即使您要執行以下操作:

if(getApplicationContext().getBean("someMap") instanceof HashMap) {
    private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
}

您可能仍會收到此警告。 問題是, getBean返回Object ,所以不知道類型是什么。 將它直接轉換為HashMap不會導致第二種情況的問題(也許在第一種情況下不會有警告,我不確定 Java 編譯器對 Java 5 的警告有多迂腐)。 但是,您將其轉換為HashMap<String, String>

HashMaps 實際上是將對象作為鍵並將對象作為值的映射,如果您願意,可以使用HashMap<Object, Object> 因此,不能保證當您獲得 bean 時它可以表示為HashMap<String, String>因為您可以擁有HashMap<Date, Calendar>因為返回的非通用表示可以具有任何對象。

如果代碼編譯通過,你可以執行String value = map.get("thisString"); 沒有任何錯誤,不要擔心這個警告。 但是,如果映射不完全是字符串鍵到字符串值的映射,您將在運行時得到ClassCastException ,因為在這種情況下泛型無法阻止這種情況的發生。

正如上面的消息所表明的, List 不能區分List<Object>List<String>List<Integer>

我已經解決了類似問題的此錯誤消息:

List<String> strList = (List<String>) someFunction();
String s = strList.get(0);

具有以下內容:

List<?> strList = (List<?>) someFunction();
String s = (String) strList.get(0);

說明:第一個類型轉換驗證對象是一個 List 而不關心其中包含的類型(因為我們無法在 List 級別驗證內部類型)。 現在需要進行第二次轉換,因為編譯器只知道 List 包含某種對象。 這會在訪問列表時驗證列表中每個對象的類型。

警告就是這樣。 一個警告。 有時警告無關緊要,有時則不然。 它們用於提醒您注意編譯器認為可能有問題但可能不是問題的某些內容。

在強制轉換的情況下,它總是會在這種情況下發出警告。 如果您絕對確定某個特定的轉換是安全的,那么您應該考慮在該行之前添加這樣的注釋(我不確定語法):

@SuppressWarnings (value="unchecked")

您收到此消息是因為 getBean 返回一個 Object 引用,並且您正在將其轉換為正確的類型。 Java 1.5 給你一個警告。 這就是將 Java 1.5 或更高版本與這樣工作的代碼一起使用的性質。 Spring 有類型安全版本

someMap=getApplicationContext().getBean<HashMap<String, String>>("someMap");

在它的待辦事項清單上。

如果您真的想擺脫警告,您可以做的一件事是創建一個從泛型類擴展的類。

例如,如果您嘗試使用

private Map<String, String> someMap = new HashMap<String, String>();

你可以像這樣創建一個新類

public class StringMap extends HashMap<String, String>()
{
    // Override constructors
}

那么當你使用

someMap = (StringMap) getApplicationContext().getBean("someMap");

編譯器確實知道(不再是通用的)類型是什么,並且不會有警告。 這可能並不總是完美的解決方案,有些人可能會爭辯說這種違背了泛型類的目的,但是您仍然在重新使用泛型類中的所有相同代碼,您只是在編譯時聲明什么類型你想用。

避免未檢查警告的解決方案:

class MyMap extends HashMap<String, String> {};
someMap = (MyMap)getApplicationContext().getBean("someMap");

下面的代碼導致類型安全警告

Map<String, Object> myInput = (Map<String, Object>) myRequest.get();

解決方法

創建一個新的地圖對象而不提及參數,因為列表中保存的對象類型未經驗證。

第 1 步:創建一個新的臨時 Map

Map<?, ?> tempMap = (Map<?, ?>) myRequest.get();

第 2 步:實例化主 Map

Map<String, Object> myInput=new HashMap<>(myInputObj.size());

第 3 步:迭代臨時 Map 並將值設置到主 Map

 for(Map.Entry<?, ?> entry :myInputObj.entrySet()){
        myInput.put((String)entry.getKey(),entry.getValue()); 
    }

我做錯了什么? 如何解決問題?

這里 :

Map<String,String> someMap = (Map<String,String>)getApplicationContext().getBean("someMap");

您使用我們通常不想使用的遺留方法,因為它返回Object

Object getBean(String name) throws BeansException;

有利於從 bean 工廠獲取(對於單例)/創建(對於原型)一個 bean 的方法是:

<T> T getBean(String name, Class<T> requiredType) throws BeansException;

使用它,例如:

Map<String,String> someMap = app.getBean(Map.class,"someMap");

將編譯但仍然帶有未經檢查的轉換警告,因為所有Map對象不一定是Map<String, String>對象。

但是<T> T getBean(String name, Class<T> requiredType) throws BeansException; 在諸如泛型集合之類的 bean 泛型類中是不夠的,因為這需要指定多個類作為參數:集合類型及其泛型類型。

在這種情況下,一般來說,更好的方法不是直接使用BeanFactory方法,而是讓框架注入 bean。

bean 聲明:

@Configuration
public class MyConfiguration{

    @Bean
    public Map<String, String> someMap() {
        Map<String, String> someMap = new HashMap();
        someMap.put("some_key", "some value");
        someMap.put("some_key_2", "some value");
        return someMap;
    }
}

bean注入:

@Autowired
@Qualifier("someMap")
Map<String, String> someMap;

另一種解決方案是,如果您發現自己@SupressWarnings("unchecked")投射同一個對象,並且不想用@SupressWarnings("unchecked")亂扔代碼,那么可以創建一個帶有注釋的方法。 通過這種方式,您可以集中演員陣容,並希望減少出錯的可能性。

@SuppressWarnings("unchecked")
public static List<String> getFooStrings(Map<String, List<String>> ctx) {
    return (List<String>) ctx.get("foos");
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM