Cast Map<String, ? extends Object> to Map<String, Object> - java

In Java 8, I am trying to cast Map<String, ? extends Object> to Map<String, Object>. I though that it would be safe given the constraint I put on the input Type (all ? must implement Object), but I get an unchecked cast warning.
Any idea where I reason wrong ? Any clean solution ? Thanks for your help !

This cast is not safe. In particular:
Map<String, ? extends Object> before;
before.put("foo", "example"); // <-- illegal
Map<String, Object> after;
after.put("foo", "example"); // <-- legal
Observe that String is not a subclass of ? extends Object because you don't know ? but it is a subclass of the more general Object thus the second call is okay.

Related

Java: cast Object to HashMap<String, Object>

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;

Update ArrayList<HashMap<String, ?>>

How do I update ArrayList<HashMap<String, ?>>?
quantity is String
values.get(position).put(KEY_POSITION, quantity);
Error:
The method put(String, capture#11-of ?) in the type
HashMap is not applicable for the arguments
(String, String)
So problem is related into logic of wildcards. Wildcard means "value type parameter can be anything" so HashMap<String, ?> is valid but here you're not able to put a String value into it.
Note: HashMap<String, ?> is same as HashMap<String, ? extends Object> so ? can be any type of class but String cannot be any type of class. This is reason of your error.
You need to make small change:
HashMap<String, ? super String>
Or as suggested #user902383 directly change1 (with same result) it into:
HashMap<String, String>
Now it will accept Strings.
1 String class is final (you can't extend from it) - for this reason it's better to use HashMap<String, String> First approach with HashMap<String, ? super String> is "useless" since you're not able to extend from String class.
The problem is you are using the wildcard "?". This is basically an unknown type. You should probably use String type instead, or Object if you need a more generic type. Like so:
ArrayList<HashMap<String, String>>
You can read more about it here: http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
and here:
http://docs.oracle.com/javase/tutorial/extra/generics/morefun.html
Hope it helps.
Good luck
this is code;
ArrayList<HashMap<String,String>> hashArray=new
hashArray.get(index).remove("key");
hashArray.get(index).put("key","value");
ok
You can try toString implemented in the type of "quantity".
Otherwise simply, quantity + ""

Generics issue with Java

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.

Java type captures for wildcards in collections

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.

Why i can't put string into a field which is something that extends Object

I have a map like the one below
final Map<String, ? extends Object> map
Can anyone tell me why this operation is not possible..?
productMap.put("min", String.valueof(34));
What should be the turnaround...
You can't add any object to a Map because the compiler knows the value is some class which extends Object, but doesn't know which one.
Map<String, ? extends Object> map = new HashMap<String, Integer>();
Object val = map.get("min"); // this is ok.
map.put("min", Integer.toString(34)); // not allowed.
Instead you can use
Map<String, Object> map = new HashMap<String, Object>();
Object val = map.get("min"); // this is ok.
map.put("min", Integer.toString(34)); // is ok.
The wildcard provides flexibility (you can now assign a HashMap<String, String> or a HashMap<String, Integer> to map) in exchange for a condition: you cannot write to map, because it doesn't know what the actual class of the values will be.
See here for a good tutorial.
You've told the compiler that the map values will be some specific subtype of Object. ? could be anything -- you could do:
Map<String,? extends Object> map = new HashMap<String,Integer>();
So String might be invalid.
You probably want the simpler Map which does allow any value.
Or you can "cheat" and do a cast which hides the generic type:
((Map)productMap).put("min", String.valueof(34));
But that trick is not best practice & to be used sparingly if at all.
By declaring the map as Map<String, ? extends Object>, you state that the second type is "anything which inherits from object". This is equivalent to declaring the map as Map<String, ?>. This declaration enables assignments like
final Map<String, ? extends Object> map = new HashMap<String, Integer>();
I think this example makes it clear why the compiler forbids inserting strings into the map: The value type is unspecified in the declaration.

Categories

Resources