Consider the following snippet:
List<Object> objs1 = Arrays.asList("one", "two");
List<String> strs1 = (List<String>)(List<?>)objs1;
assert strs1 == objs1; //compile error
Since ==
only compares the object identity - why is it give compilation error when it is just the matter of cast.
While consider the following snippet - here the assertion does not fail :
String str = "this is it";
Object oo = str;
assert str == oo; //asserts true
Here again - we are doing just the same thing but it works fine.
- Why does
==
operator behave on the basis of reference type that is holding the object even when the object being held is the same?
You can set and compare incompatible references at runtime because of type erasure. Generics appeared in java 5 (named 1.5 at this time) and under the hood the jvm still manage to use
List<Object>
at runtime. What the compiler say is thatList<Object>
is not comparable withList<String>
even if you manipulate the same reference.In fact semantically a
List<Object>
could not contain aList<String>
so you came into an illogical situationThe "edge-case" in fact is that you tricked the compiler with the double cast
List<String> strs = (List<String>)(List<?>)objs;
List<Object>
is not aList<String>
as should should not be assigned. On the 2nd line the compiler let you use a cast that shouldn't be possible :If you want a super type of both
List<String>
andList<Object>
it must beList<? extends Object>
and then the code is correct for both compiler and runtime .This works and is semantically correct
Now speaking of type erasure, it can lead to similar problems:
Here I can affect a List of Integer in a List of String variable and I get no classCastException, because of type erasure. If the type protection was perfect, I shouldn't be able 1. to cast A
List<Integer>
to aList<String>
2. to affect this value at runtime.