Why is it possible to insert a String
into a List<Integer>
in the following code? I have a class which inserts numbers into a List of Integers:
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(2);
list.add(3);
list.add(4);
Inserter inserter = new Inserter();
inserter.insertValue(list);
System.out.print(list);
}
}
Then I have a separate class which inserts a String
into a List
, with the numeric string value "42"
:
public class Inserter {
void insertValue(List list)
{
list.add(new String("42"));
}
}
Why does the compiler not raise a compiler error, or the runtime throw a runtime exception such as *CastException
, when I add the String
to the List of Integer? Also, why does System.out.print(list)
produce output like the following without throwing any exceptions?
[2, 3, 4, 42]
What is the fundamental reason that allows all this to happen?
Let's discuss certain ways in which we can perform string append operations in the list of integers. Method #1 : Using + operator + list conversion In this method, we first convert the string into a list and then perform the task of append using + operator.
But a cast does not need to be inserted to call the Object overload, so an exception doesn't throw. Using a raw type List to add String to a List<Integer> is called heap pollution. The code in the question is an example of why heap pollution is so bad: because sometimes we don't find out about it.
Any Collection can be passed as an argument to the constructor as long as its type extends the type of the ArrayList , as String extends Object . The constructor takes a Collection , but List is a subinterface of Collection , so you can just use the List<String> .
This is probably an example to illustrate type erasure for generics (I recommend reading that link to fully understand this and the crucial role it plays in Java generics).
list
is declared as a List<Integer>
listValue
it is cast to a raw type
Object
†
print
command simply calls toString
on the list, which doesn't mind what it contains, and so it prints the elements including the string.If you want to see an exception, try adding a line:
Integer myInt = list.get(3); // try to get the string
This will throw a ClassCastException
as the compiler, during erasure, inserts casts where necessary to protect type safety.
† Casts of parameterized types, such as List<Integer>
to the raw type, such as List
, give you a compiler warning to tell you that exactly this kind of problem might be about to happen. You can suppress that warning with @SuppressWarnings("unchecked")
(or "rawtypes"
, if it is available to you). This is a good way of "acknowledging" that you are about to do something that will lead to an unchecked runtime exception, and also helps tell other future coders that something funky might be about to happen. E.g.:
void insertValue(@SuppressWarnings("unchecked") List list)
{
list.add(new String("42"));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With