I have a Map object stored in a sessionScope. If I cast it to the following object:
Map<String,Object> mapItem = (Map<String, Object>) entry.getValue();
I get a Type safety warning. Type safety:
Unchecked cast from Object to Map<String,Object>
I tried to cast the scope variable to an object and check what instance the object is but then I can not directly check if it is of Map<String,Object>. So I wonder how I should handle this further?
Object obj = entry.getValue();
if(obj instanceof Map<?, ?>) {
//not sure what to do here
}
Any help is highly appreciated!
I think the issue is with the generics applied to the original Map your Entry is from. Consider these examples
Map<String, Object> map = new HashMap<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
Map<String, Object> mapItem = (Map<String, Object>) entry.getValue(); // Will have unchecked warning
}
// Declare another map specifying the values type more specifically
Map<String, Map<String, Object>> map2 = new HashMap<>();
for (Map.Entry<String, Map<String, Object>> entry : map2.entrySet()) {
Map<String, Object> mapItem = entry.getValue(); // Will not require cast
}
The issue occurs because the compiler doesn't know anything more about the values type than its an Object. You know its a Map<String, Object> so you can tell the compiler that with generics and it will enforce that in the rest of the code.
It's not easy to achieve that;
Look at how to handle it in Gson:
Type mapType = new TypeToken<Map<String, Object>>(){}.getType();
new Gson().fromJson("{}", mapType);
Related
I am trying to cast an Object to HashMap<String, Object> in a neat, robust way. So far, every way I tried produces compiler warnings or errors. What is the proper way to do it? I have checked the internet and tried the following:
HashMap<String, Object> map = (HashMap<String, Object>) object;
The code above gives an unchecked conversion warning.
HashMap<String, Object> map = new HashMap<>();
if (object instanceof Map<String, Object>){
map = (Map<String, Object>) object;
}
The code above gives an error, which says that objects cannot be compared to parameterized collections.
HashMap<String, Object> map = new HashMap<>();
if (object instanceof Map){
Map genericMap = (Map) object;
for (Object key : genericMap.keySet()){
if (key instanceof String){
map.put((String) key, genericMap.get(key));
}
else{
throw new KeyException();
}
}
}
The code above yields a warning that "Map is a raw type. References to generic type Map<K,V> should be parameterized."
So what would be the proper way to do this? Thank you in advance!
I am trying to cast an Object to HashMap<String, Object> in a neat, robust way. So far, every way I tried produces compiler warnings or errors. What is the proper way to do it?
There is no proper way to do it, supposing that "proper" implies both useful and type safe. Casting is the antithesis of type safety. Other than casts for arithmetic purposes, a safe cast is an unnecessary one.
There is not enough information to determine how to achieve what ultimately you are after, but generally speaking, that sort of thing revolves around writing true generic code instead of using type Object to funnel objects of unrelated type into the same methods, using instanceof to determine what you actually have, or casting.
Just add #SuppressWarnings("unchecked") to your method
#SuppressWarnings("unchecked")
public void myMethod(){
...
}
and you should be able to use
HashMap<String, Object> map = (HashMap<String, Object>) object;
I have an Object which is of type Map<String, String> which has few entries. I expected to get a ClassCastException while casting this object to Map<String, Integer>. But the cast was successful. Why is it that this did not throw any exception?
Map<String, String> map1 = new HashMap<>();
map1.put("key1", "value1");
map1.put("key2", "value2");
Object o = map1;
Map<String, Integer> map2 = (Map<String, Integer>) o;
Edit: Casting from o not map1.
Generic-checking is made at compile time,while casting checking is done at the time of running the program.So You had got casting exception at run time.
You parse it as Integer.parseInt(String) and put the value into map2.
Are you sure that's right?
Your example fails to compile:
Error:(21, 60) java: incompatible types: java.util.Map<java.lang.String,java.lang.String> cannot be converted to java.util.Map<java.lang.String,java.lang.Integer>
However, changing from map1 to o, does compile:
//...
Object o = map1;
Map<String, Integer> map2 = (Map<String, Integer>) o;
Perhaps you're looking for something like this?
Map<String, String> map1 = new HashMap<>();
map1.put("key1", "1");
map1.put("key2", "2");
Map<String, Integer> map2 = new HashMap<>();
map1.forEach((key,value) -> map2.put(key, Integer.parseInt(value)));
Why does this cast work?
import java.util.HashMap;
import java.util.Map;
public class TestMap {
public static void main(String[] args) {
Map<String, Map<String, Map<String, Map<String,Integer>>>> resultMap = new HashMap<>();
Map<String, Object> aMap = new HashMap<String, Object>();
Map<String, Integer> hiddenMap = new HashMap<String, Integer>();
hiddenMap.put("fortytwo", 42);
aMap.put("key", hiddenMap);
resultMap = (Map<String, Map<String, Map<String, Map<String, Integer>>>>) aMap.get("key");
System.out.println(resultMap);
}
}
also this:
Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>> resultMap = new HashMap<>();
...
resultMap = (Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>>) aMap.get("key");
and so on...
How does this happen that the hidden map which is Map<String, Integer> gets successfully cast to Map<String, Map<String, Map<String, Map<String,Integer>>>> resultMap?
Always prints:
{fortytwo=42}
Also this works (Map instead of Map):
public static void main(String[] args) {
Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>> resultMap = new HashMap<>();
Map<String, Map> aMap = new HashMap<String, Map>();
Map<String, Integer> hiddenMap = new HashMap<String, Integer>();
hiddenMap.put("fortytwo", 42);
aMap.put("key", hiddenMap);
resultMap = (Map<String, Map<String, Map<String, Map<String,Map<String,Integer>>>>>) aMap.get("key");
System.out.println(resultMap);
}
EDIT: So as #shizhz says, it is because of Type Erasure of course! So the code above is equivalent to:
Map resultMap = new HashMap();
Map aMap = new HashMap();
Map hiddenMap = new HashMap();
hiddenMap.put("fortytwo", 42);
aMap.put("key", hiddenMap);
resultMap = (Map) aMap.get("key");
Which also works
Because java generics is used at compile time to provide tighter type checks, the type parameter is erased by compiler according Type Erasure rules:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Insert type casts if necessary to preserve type safety.
Generate bridge methods to preserve polymorphism in extended generic types.
In code Map<String, Map> aMap = new HashMap<String, Map>();, the value in aMap is a raw type Map, which means the compiler has no idea what's the type it contains, when you try to cast a raw type of Map to any generics type of Map like Map<String, Integer>, the best compiler can do is giving you a warning. The generic type is erased at compile time and type cast will be generated when you get value from a generic map, so you can only get a runtime ClassCastException exception if the type mismatchs.
Let's have a look at the following example:
public static void main(String[] args) {
Map map = new HashMap();
map.put("hello", "world");
map.put(new Integer(1), 1);
map.put(new Object(), Lists.newArrayList("hello"));
Map<String, Integer> m = (Map<String, Integer>) map;
System.out.println(m);
Integer i = m.get("hello");// ClassCastException happens at here at runtime
}
I'm trying to convert a Map containing all kinds of keys and values to Map<String, Integer> but there's no compile error, after type erasure, the above code is actually equivalent to:
public static void main(String[] args) {
Map map = new HashMap();
map.put("hello", "world");
map.put(new Integer(1), 1);
map.put(new Object(), Lists.newArrayList("hello"));
Map m = (Map) map;
System.out.println(m);
Integer i = (Integer)m.get("hello");
}
Now you can easily tell why the last line caused ClassCastException.
Since you've declared aMap as Map<String, Object>, the compiler cannot tell if the values won't indeed be of type Map<String, Map<String, Map<String,Integer>>>. It will just give you an "Unchecked cast" warning to let you think about the consequences.
The cast works unless you're actually trying to do something with the values:
resultMap.get("fortytwo").isEmpty();
will result in
Exception in thread "main" java.lang.ClassCastException:
java.lang.Integer cannot be cast to java.util.Map
If you had declared aMap as Map<String, Map<String, Map<String, Map<String, Map<String, Integer>>>>> you wouldn't be able to put hiddenMap in it in the first place.
An API I am using has a method that returns a Map<String, Object>, but I know the Object's are String's in this case, so I want it as a Map<String, String>.
But for some reason I can't just cast it, Java says Map<String, Object> cannot be casted to Map<String, String>, for some reason.
I used:
Map<String, Object> tempMap = someApiMethodReturningAMap();
Map<String, String> map = new HashMap<String, String>();
for (String i : tempMap.keySet()) {
map.put(i, String.valueOf(tempMap.get(i)));
}
as a workaround, but is there an easier way?
Well you can't safely cast it to a Map<String, String> because even though you know you've only got strings as the values, the compiler doesn't. That's like expecting:
Object x = "foo";
String y = x;
to work - it doesn't; you need to explicitly cast.
Likewise you can explicitly cast here, too, if you go via Object:
Map<String, Object> x = ...;
Map<String, String> y = (Map<String, String>) (Object) x;
Now you'll get a warning saying that it's an unchecked cast, because unlike the earlier "object to string" cast, there's no execution-time check that it's really valid. Type erasure means that a map doesn't really know its key/value types. So you end up with checking only being done when elements are fetched:
import java.util.*;
class Test {
public static void main(String[] args) {
Map<String, Object> x = new HashMap<>();
x.put("foo", "bar");
x.put("number", 0);
Map<String, String> y = (Map<String, String>) (Object) x;
// This is fine
System.out.println(y.get("foo"));
// This goes bang! It's trying to cast an Integer to a String
System.out.println(y.get("number"));
}
}
So if you really want to avoid creating a new map, this "cast via Object" will work - but it's far from ideal.
Your approach is safer, although you can make it slightly more efficient by avoiding the lookup:
public static Map<String, String> copyToStringValueMap(
Map<String, Object> input) {
Map<String, String> ret = new HashMap<>();
for (Map.Entry<String, Object> entry : input.entrySet()) {
ret.put(entry.getKey(), (String) entry.getValue());
}
return ret;
}
A Java 8 solution:
private Map<String, String> stringifyValues(Map<String, Object> variables) {
return variables.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> (String) e.getValue()));
}
Good solutions here, but I want to add another one that taking into consideration handling null values:
Map<String,Object> map = new HashMap<>();
Map<String,String> stringifiedMap = map.entrySet().stream()
.filter(m -> m.getKey() != null && m.getValue() !=null)
.collect(Collectors.toMap(Map.Entry::getKey, e -> (String)e.getValue()));
How can I safely cast a Map to a hash Map?
I want to avoid class cast exception
HashMap<String, String> hMap;
public void setHashMap(Map map){
hMap = (HashMap<String, String>) map;
}
You can make a (shallow) copy:
HashMap<String, String> copy = new HashMap<String, String>(map);
Or cast it if it's not a HashMap already:
HashMap<String, String> hashMap =
(map instanceof HashMap)
? (HashMap) map
: new HashMap<String, String>(map);
In general, you cannot typecast a Map to a HashMap without risk of a class-cast exception. If the Map is a TreeMap then the cast will (and must) fail.
You can avoid the exception by making using instanceof to check the type before you cast, but if the test says "not a HashMap" you are stuck. You won't be able to make the cast work.
The practical solutions are:
declare hMap as a Map not a HashMap,
copy the Map entries into a newly created HashMap, or
(yuck) create a custom HashMap subclass that wraps the real map.
(None of these approaches will work in all cases ... but I can't make a specific recommendation without more details of what the map is used for.)
And while you are at it, it might be appropriate to lodge a bug report with the providers of the problematic library. Forcing you to use a specific Map implementation is (on the face of it) a bad idea.
Your function should be as below to avoid any kind of exception such as ClassCastException or NullPointerException. Here any kind of Map object will be assigned to HashMap into your field of the class.
public void setHashMap(Map<String, String> map) {
if (map != null && map instanceof HashMap<?, ?>) {
hMap = (HashMap<String, String>) map;
} else if (map != null) {
hMap.putAll(map);
}
}
You should not cast to HashMap! Cast to Map!
If you really have a reason for your question, then, you have to create a new HashMap in case Map is not an instance of Map.
But this is a bad idea.
You can do:
if (map instanceof HashMap) {
hMap = (HashMap<String, String>) map;
} else {
//do whatever you want instead of throwing an exception
}
or just surround the cast with a try/catch and capture the exception when it happens.
If you're going to always assume that it's a HashMap<String, String>, why not just do this?
HashMap<String, String> hMap;
public void setHashMap(HashMap<String, String> map){
hMap = map;
}
If you want something more generic that will accept any Map:
public void setHashMap(Map<String, String> map){
if (map != null)
hMap = new HashMap<String, String>(map);
else
hMap = new HashMap<String, String>();
}
No casting required. Also, your example was missing the return type. I've assumed that you meant to put void.