Skip to content
Advertisement

Unchecked Cast warning – shows up for Type parameters but not for Concrete types?

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 the Supertype will ever match the SubType.
  • Then over here – why is (Double) (Number)a not being flagged as the Unchecked 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 the SubType.

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.

Advertisement