Consider the following snippet:
Integer a = Integer.valueOf(23); Double d = (Double) (Number) a; //not unchecked cast List<String> stringList = new ArrayList<>(); List<Integer> integerList = (List<Integer>)(List<?>)stringList; //unchecked cast
- From what I understand – whenever we convert
Supertype -> Subtype
Unchecked cast is flagged as the compiler does not know until the runtime if the type represented by theSupertype
will ever match theSubType
. - Then over here – why is
(Double) (Number)a
not being flagged as theUnchecked Cast
?
Advertisement
Answer
Unchecked cast is flagged as the compiler does not know until the runtime if the type represented by the
Supertype
will ever match theSubType
.
That is incorrect. Not even the runtime knows whether your list is a ArrayList<String>
or a ArrayList<Integer>
. As far as the runtime is concerned, your list is an ArrayList
(This is because of type erasure). This is why casting a List<?>
to a List<String>
cannot be checked by the runtime. The runtime does not know what a List<String>
is – it only knows about List
. To the runtime, no checks are needed, since you are just casting from List
to List
, which always succeeds. In fact, nothing is done for this cast at runtime – it is a completely unchecked cast.
As a result, this code runs without throwing an exception:
List<String> stringList = new ArrayList<>(); stringList.add("foo"); List<Integer> integerList = (List<Integer>)(List<?>)stringList; System.out.println(integerList.size());
It will throw an exception, however, if you did:
List<String> stringList = new ArrayList<>(); stringList.add("foo"); List<Integer> integerList = (List<Integer>)(List<?>)stringList; System.out.println(integerList.get(0) - 1);
Now, you are getting an integer out of the list, and doing some integer-specific operations to it. But the list contains "foo"
, which isn’t an integer. The compiler inserts an implicit cast to cast integerList.get(0)
to Integer
, and that cast failed. Note that this is a checked cast, because the runtime knows about the type Integer
.
The cast from Number
to Double
is also checked, because the runtime knows about Double
.
Side note: there are also “partially unchecked casts”, which are things like casting from Object
to ArrayList<String>
. The runtime can check whether the Object
is an ArrayList
, but it cannot check whether it is an array list of strings.
See all the rules for what is checked and what is unchecked here.