Adding a String to the <Integer> List
The result of the following code:
public class ListIntegerDemo1 { public static void addToList(List list) {list.add("0067");list.add("bb");} public static void main(String[] args) { List<Integer> list = new ArrayList<>(); addToList(list); System.out.println(list.get(0)); } } there is "0067" .
I would expect a RuntimeException or the like as I add a string to the list of integers.
Why does it work without any problems?
There are two reasons why an exception is not thrown, depending on how we look at this particular example. It also depends on why, or where, we think the exception should be thrown. (If we think we need to quit altogether, since it is clear that Java thinks differently).
The code in the question is complicated, even if we know why add does not throw an exception, because one reason is related to how Java selects overloads. I would recommend running the following program to better understand:
class Example { static void m(int arg) { System.out.println("int " + arg); } static void m(Object arg) { System.out.println("Object " + arg); } public static void main(String[] args) { m(new Integer(0)); } } Prints Object 0 . What for? Since Java seeks overload in three steps: *
- At the first stage, overload occurs without the permission of boxing, unpacking and varargs.
- The second stage is looking for overload, allowing boxing and unpacking.
- The third stage requires overload, allowing boxing, unpacking and varargs.
These stages are "short circuited", so in the case of m(new Integer(0)) , stage 1 detects an Object overload before considering an int overload that needs to be unpacked. Since an overload has been detected, step 2 has not been verified.
The same thing happens for ListIntegerDemo1 , where System.out.println(Object) is selected.
Generics erased , which means that:
list.getactually returns anObject.- To assign its result to
Integer, the cast is inserted by the compiler like(Integer) list.get(0).
But the throw does not need to be inserted to cause an Object overload, so the exception is not thrown.
Using the raw List type to add a String to a List<Integer> is called heap pollution. The code in the question is an example of why the heap pollution is so bad: because sometimes we don’t know about it. Sometimes we find out much later and don’t know where the error was actually caused.
An exception will be thrown if we do this:
Integer i = list.get(0); System.out.println(i); Because in this case, casting is required to complete the task.
And also if we did this:
public class ListIntegerDemo1 { public static void addToList(List list) {list.add(67);list.add(0xbb);} public static void main(String[] args) { List<String> list = new ArrayList<>(); addToList(list); System.out.println(list.get(0)); } } Because in this case, System.out.println(String) is selected as more specific, and a listing is inserted.
We could also prevent our List<Integer> from being polluted in unsafe code with Collections#checkedList :
List<Integer> list = Collections.checkedList( new ArrayList<>(), Integer.class); In this case, the exception will be thrown from the add call inside addToList .
Of course, the best solution is not to use raw types:
static void addToList(List<String> list) { list.add("0067"); list.add("bb"); } Now we cannot pass the List<Integer> method.
addToList can also accept List<? super String> List<? super String> .
* This is described in detail in 15.12.2 .