Type safety: Unchecked cast

2019-01-02 14:46发布

In my spring application context file, I have something like:

<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>

In java class, the implementation looks like:

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

In Eclipse, I see a warning that says:

Type safety: Unchecked cast from Object to HashMap

What did I do wrong? How do I resolve the issue?

9条回答
无与为乐者.
2楼-- · 2019-01-02 14:56

If you really want to get rid of the warnings, one thing you can do is create a class that extends from the generic class.

For example, if you're trying to use

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

You can create a new class like such

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

Then when you use

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

The compiler DOES know what the (no longer generic) types are, and there will be no warning. This may not always be the perfect solution, some might argue this kind of defeats the purpose of generic classes, but you're still re-using all of the same code from the generic class, you're just declaring at compile time what type you want to use.

查看更多
若你有天会懂
3楼-- · 2019-01-02 14:57

Below code causes Type safety Warning

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

Workaround

Create a new Map Object without mentioning the parameters because the type of object held within the list is not verified.

Step 1: Create a new temporary Map

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

Step 2: Instantiate the main Map

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

Step 3: Iterate the temporary Map and set the values into the main Map

 for(Map.Entry<?, ?> entry :myInputObj.entrySet()){
        myInput.put((String)entry.getKey(),entry.getValue()); 
    }
查看更多
十年一品温如言
4楼-- · 2019-01-02 15:00

As the messages above indicate, the List cannot be differentiated between a List<Object> and a List<String> or List<Integer>.

I've solved this error message for a similar problem:

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

with the following:

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

Explanation: The first type conversion verifies that the object is a List without caring about the types held within (since we cannot verify the internal types at the List level). The second conversion is now required because the compiler only knows the List contains some sort of objects. This verifies the type of each object in the List as it is accessed.

查看更多
余生请多指教
5楼-- · 2019-01-02 15:03

The solution to avoid the unchecked warning:

class MyMap extends HashMap<String, String> {};
someMap = (MyMap)getApplicationContext().getBean("someMap");
查看更多
荒废的爱情
6楼-- · 2019-01-02 15:05

Well, first of all, you're wasting memory with the new HashMap creation call. Your second line completely disregards the reference to this created hashmap, making it then available to the garbage collector. So, don't do that, use:

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

Secondly, the compiler is complaining that you cast the object to a HashMap without checking if it is a HashMap. But, even if you were to do:

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

You would probably still get this warning. The problem is, getBean returns Object, so it is unknown what the type is. Converting it to HashMap directly would not cause the problem with the second case (and perhaps there would not be a warning in the first case, I'm not sure how pedantic the Java compiler is with warnings for Java 5). However, you are converting it to a HashMap<String, String>.

HashMaps are really maps that take an object as a key and have an object as a value, HashMap<Object, Object> if you will. Thus, there is no guarantee that when you get your bean that it can be represented as a HashMap<String, String> because you could have HashMap<Date, Calendar> because the non-generic representation that is returned can have any objects.

If the code compiles, and you can execute String value = map.get("thisString"); without any errors, don't worry about this warning. But if the map isn't completely of string keys to string values, you will get a ClassCastException at runtime, because the generics cannot block this from happening in this case.

查看更多
爱死公子算了
7楼-- · 2019-01-02 15:06

The problem is that a cast is a runtime check - but due to type erasure, at runtime there's actually no difference between a HashMap<String,String> and HashMap<Foo,Bar> for any other Foo and Bar.

Use @SuppressWarnings("unchecked") and hold your nose. Oh, and campaign for reified generics in Java :)

查看更多
登录 后发表回答