Excuse me if this was asked before (I'm almost sure) but I can't find the right keywords or dig it up from the pile of unrelated generics questions.
This works:
Object a = null;
Set<Object> b = (Set<Object>)a;
so why does this give a compiler error?
Set<Object> c = null;
Set<Set<Object>> d = (Set<Set<Object>>)c;
and why is this simple workaround valid?
Set<Object> e = null;
Set<?> f = (Set<?>)e;
Set<Set<Object>> g = (Set<Set<Object>>)f;
I have a library method that returns a Map<Number, Object>
of a preprocessed JSON message. After manual deep-type-checking, I need to cast and return it as Map<Number, Map<String, Map<String, Object>>>
, expecting an unchecked cast warning which I can ignore because I've just checked it.
However I'm getting the compiler error:
Error:(61, 10) java: Cannot cast from java.util.Map<java.lang.Number,java.lang.Object> to java.util.Map<java.lang.Number,java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.Object>>>
or in a more readable form (based on the error in the IDE):
Inconvertible types; cannot cast 'Map<Number,Object>' to 'Map<Number,Map<String,Map<String,Object>>>'
)
[edit] Changed from a how-to question to a why-question.
Note that since generic types are erased at runtime, nothing substantial actually happens when you do these casts. It's really all boils down to "does the compiler allow this?"
One of the aims of generics is to ensure type safety. If you could, for some reason, assign List<List<Object>>
to List<Object>
, you'd lose type safety:
List<List<Object>> listOfLists = new ArrayList<>();
List<Object> list = listOfLists; // suppose this is valid
list.add(new Object());
listOfLists.get(0); // what happens here?
(I am using lists here for convenience. You can easily apply this to maps or sets or any other generic type)
listOfLists.get(0)
would return an instance of Object
, as that is what we added, but it wouldn't! Because its type is List<List<Object>>
! This is why being able to cast like this undermines type safety.
However, the two casts below are valid:
List<Object> e = null;
List<?> f = (List<?>)e;
List<List<Object>> g = (List<List<Object>>)f;
List<?> f
means "f
is a List of some class that extends Object
, but that class could be anything". Note that List<?>
is different from List<Object>
. You can't do f.add("String")
because ?
is not necessarily a String
, is it?
The first cast is allowed for the same reason why most people (incorrectly) believes that List<String>
can be assigned to List<Object>
. In fact, this cast is not needed. A List<String>
is a List
of stuff that extends Object
.
The second cast is you telling the compiler that you know what the ?
is, and it is List<Object>
. The compiler warns you that you might be wrong about what ?
is by issuing an unchecked warning.
In a sense, you are kind of asking why you can't cast directly from Integer
to String
, but can first cast from Integer
to Object
then to String
.
I feel like your confusion might be caused by a misunderstanding of the differences between <Object>
and <?>
. This post might be worth reading.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments