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;
Related
I'm using Eclipse to help me clean up some code to use Java generics properly. Most of the time it's doing an excellent job of inferring types, but there are some cases where the inferred type has to be as generic as possible: Object. But Eclipse seems to be giving me an option to choose between a type of Object and a type of '?'.
So what's the difference between:
HashMap<String, ?> hash1;
and
HashMap<String, Object> hash2;
An instance of HashMap<String, String> matches Map<String, ?> but not Map<String, Object>. Say you want to write a method that accepts maps from Strings to anything: If you would write
public void foobar(Map<String, Object> ms) {
...
}
you can't supply a HashMap<String, String>. If you write
public void foobar(Map<String, ?> ms) {
...
}
it works!
A thing sometimes misunderstood in Java's generics is that List<String> is not a subtype of List<Object>. (But String[] is in fact a subtype of Object[], that's one of the reasons why generics and arrays don't mix well. (arrays in Java are covariant, generics are not, they are invariant)).
Sample:
If you'd like to write a method that accepts Lists of InputStreams and subtypes of InputStream, you'd write
public void foobar(List<? extends InputStream> ms) {
...
}
By the way: Joshua Bloch's Effective Java is an excellent resource when you'd like to understand the not so simple things in Java. (Your question above is also covered very well in the book.)
Another way to think about this problem is that
HashMap<String, ?> hash1;
is equivalent to
HashMap<String, ? extends Object> hash1;
Couple this knowledge with the "Get and Put Principle" in section (2.4) from Java Generics and Collections:
The Get and Put Principle: use an
extends wildcard when you only get
values out of a structure, use super
wildcard when you only put values into
a structure, and don't use a wildcard
when you both get and put.
and the wild card may start making more sense, hopefully.
It's easy to understand if you remember that Collection<Object> is just a generic collection that contains objects of type Object, but Collection<?> is a super type of all types of collections.
The answers above covariance cover most cases but miss one thing:
"?" is inclusive of "Object" in the class hierarchy. You could say that String is a type of Object and Object is a type of ?. Not everything matches Object, but everything matches ?.
int test1(List<?> l) {
return l.size();
}
int test2(List<Object> l) {
return l.size();
}
List<?> l1 = Lists.newArrayList();
List<Object> l2 = Lists.newArrayList();
test1(l1); // compiles because any list will work
test1(l2); // compiles because any list will work
test2(l1); // fails because a ? might not be an Object
test2(l2); // compiled because Object matches Object
You can't safely put anything into Map<String, ?>, because you don't know what type the values are supposed to be.
You can put any object into a Map<String, Object>, because the value is known to be an Object.
Declaring hash1 as a HashMap<String, ?> dictates that the variable hash1 can hold any HashMap that has a key of String and any type of value.
HashMap<String, ?> map;
map = new HashMap<String, Integer>();
map = new HashMap<String, Object>();
map = new HashMap<String, String>();
All of the above is valid, because the variable map can store any of those hash maps. That variable doesn't care what the Value type is, of the hashmap it holds.
Having a wildcard does not, however, let you put any type of object into your map. as a matter of fact, with the hash map above, you can't put anything into it using the map variable:
map.put("A", new Integer(0));
map.put("B", new Object());
map.put("C", "Some String");
All of the above method calls will result in a compile-time error because Java doesn't know what the Value type of the HashMap inside map is.
You can still get a value out of the hash map. Although you "don't know the value's type," (because you don't know what type of hash map is inside your variable), you can say that everything is a subclass of Object and, so, whatever you get out of the map will be of the type Object:
HashMap<String, Integer> myMap = new HashMap<>();// This variable is used to put things into the map.
myMap.put("ABC", 10);
HashMap<String, ?> map = myMap;
Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map).
System.out.println(output);
The above block of code will print 10 to the console.
So, to finish off, use a HashMap with wildcards when you do not care (i.e., it does not matter) what the types of the HashMap are, for example:
public static void printHashMapSize(Map<?, ?> anyMap) {
// This code doesn't care what type of HashMap is inside anyMap.
System.out.println(anyMap.size());
}
Otherwise, specify the types that you need:
public void printAThroughZ(Map<Character, ?> anyCharacterMap) {
for (int i = 'A'; i <= 'Z'; i++)
System.out.println(anyCharacterMap.get((char) i));
}
In the above method, we'd need to know that the Map's key is a Character, otherwise, we wouldn't know what type to use to get values from it. All objects have a toString() method, however, so the map can have any type of object for its values. We can still print the values.
I need to convert raw Map to Map<string,string>, and I think I have to first convert the raw map to Map<Object,Object> and then convert it again to Map<String,String>.
code snippet goes like below.
Map obj1 = new HashMap();
obj1.put("key1", 1);
obj1.put("key2", false);
obj1.put("key3", 3.94f);
Map<Object, Object> obj2 = obj1;
Map<String, String> obj = new HashMap<String,String>();
for (Map.Entry<Object, Object> entry: obj2.entrySet()) {
obj.put(entry.getKey().toString(), entry.getValue().toString());
}
I guess it would work in any condition but I want to hear from others about possible danger of this code.(any possiblities for ClassCastException for example?)
Please also let me know if you have a better idea.
-- revised code
Map obj1 = new HashMap();
obj1.put(2, 1);
obj1.put(true, false);
obj1.put(4.4f, 3.94f);
Map<String, String> obj = new HashMap<String,String>();
for (Object k : obj1.keySet()){
obj.put(k.toString(), obj1.get(k).toString());
}
Since raw Map entries will contain key/value of Objects anyway, I think I don't need temporary Map<Object,Object>. Just iterating over each item works well and I don't see any issues so far.
If You Look out the Definition of HashMap in jdk 1.4 It was earlier Implements using Object Class when generics Concept not came.
When generics is Introduced this object is Replaced with <T>. But If you Still don't use Generics Type Safe then Internally this Statement new HashMap() reflects a instance of <Object, Object>. Better To use directly a
a new HashMap() is better idea. There should no need of Map <Object, Object> obj2.
So, GO For this.. a better approach.
Map obj1 = new HashMap();
obj1.put("key1", 1);
obj1.put("key2", false);
obj1.put("key3", 3.94f);
Map<Object, Object> obj2 = obj1;
Map<String, String> obj = new HashMap<String,String>();
for (Object obj_Entry : obj1.entrySet()) {
Map.Entry entry = (Map.Entry) obj_Entry; // This will Work Fine all Time.
obj.put(entry.getKey().toString(), entry.getValue().toString());
}
Your code will not generate ClassCastExceptions. Actually you are not doing any casting here. You just call the toString() method of every key/value pair to make it a string. As long as toString() returns a valid value of your objects. Your code will be fine.
But your code may produce NullPointerExceptions if your obj1 contain null keys or objects
obj1.put(null, "null value")
Also note that some key collisions may occur if toString() methods return same String value for two keys. This is unlikely but it is possible.
Here is my code snippet:
Map<String, ? extends Object> data = this.aub.getData();
//... some code ...
data.put("ip_macs", new LinkedList<Object>()); //gets error
The error that I get at the marked line is (the message is taken from Eclipse IDE):
The method put(String, capture#3-of ? extends Object) in the type Map is not applicable for the arguments (String, LinkedList)
Does anyone have any idea why that? As long as LinkedList is a subtype of Object, I think that error does not have any reason to appear. Where do I misunderstand this issue?
Specify the generics type as
Map<String, Object> data = this.aub.getData();
data.put("ip_macs", new LinkedList<Object>()); // Compiles
When you say Map<String, ? extends Object> it means a Map whose key is of type String and the value extends Object but its type is unknown ?. Since, the type is not known it's unsafe to insert a LinkedList object there.
Basically, the compiler is trying to prevent this:
Map<String, String> mapOfStrings = new HashMap<String, String>();
mapOfStrings.add("string", "value");
Map<String, ? extends Object> map = mapOfStrings; // Compiles
map.add("string", 1); // ERROR!
If this was allowed, you just circumvented the type safety offered by generics.
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.
I would like to do this (minimal repro):
String key = "foo";
Object value = new Bar();
if (target instanceof Map<?,?>) {
Map<?,?> map = (Map<?,?>)target;
map.put(key, value);
...
But I am told this:
The method put(capture#25-of ?, capture#26-of ?) in the type
Map is not applicable for the
arguments (String, Object)
It seems like String and Object should both be okay here. My question has two parts: (1) why? And (2) how can I make something like this work?
The problem is that collections that use unbounded wildcards don't allow elements to be added to them. If they did, you could cast the collection to have more specific type parameters, and all of a sudden the type-safety that generics are supposed to offer is gone:
Map<?,?> map = (Map<?,?>)target;
map.put(key, value); // Not actually allowed
Map<String, String> evilMap = (Map<String, String>)map;
String barAsString = evilMap.get(key); // But it's actually a Bar!
Map<?, ?> really means, Map<? extends Object, ? extends Object> According to Java typesystem, you can only get values of type parameters specialized with ? extends wildcard from method calls. You can't pass it to the methods.
P.S. Probably you want Map<Object, Object>
This blog post provides the answer. In short, the compiler doesn't know if Map<?, ?> is really a Map<String, Object> or, say, a Map<Integer, Double>, so it can't guarantee type safety for you.