...
If the method addToList()
is externally defined (such as in a library or as an upcall method) and cannot be changed, the same compliant method printOne()
can be used, but no warnings result if addToList(1)
is used instead of addToList(1.0)
. Great care must be taken to ensure type safety when generics are mixed with nongeneric code.
Noncompliant Code Example (List of lists)
Heap pollution occurs because Java does not enforce type correctness on lists of lists. This program happily tries to build a list of list of strings, while trying to sneak a single integer into the list. While the Java compiler emits several warnings, this program compiles and runs until it tries to extract the integer 42 from a List<String>
, and throws a ClassCastException
.
Code Block | ||||
---|---|---|---|---|
| ||||
public static void inspect(List<List<String>> l) {
for (List<String> list : l) {
for (String s : list) { // ClassCastException on 42
System.out.println(s);
}
}
}
public static void main(String[] args) {
List<String> s = Arrays.asList("foo");
List i = Arrays.asList(42); // warning about raw types
List<List<String>> l = new ArrayList<List<String>>();
l.add(s);
l.add(i); // warning about unchecked convertion
inspect(l);
}
}
h2. Noncompliant Code Example (Array of lists)
The problem persists if the function paramter is replaced with an array of lists
{code:bgColor=#ffcccc|lang=java}
public static void inspect(List<String>[] l) {
for (List<String> list : l) {
for (String s : list) { // ClassCastException on 42
System.out.println(s);
}
}
}
public static void main(String[] args) {
List<String> s = Arrays.asList("foo");
List i = Arrays.asList(42); // warning about raw types
inspect(new List[] {s, i}); // warning about unchecked convertion
}
}
h2. Noncompliant Code Example (Variadic Arguments)
The problem persists if the function parameter is changed to a variadic argument of {{List<String>}}. During type erasure, the compiler translates this to a single array of objects.
{code:bgColor=#ffcccc|lang=java}
class ListInspector {
public static void inspect(List<String>... l) {
for (List<String> list : l) {
for (String s : list) { // ClassCastException on 42
System.out.println(s);
}
}
}
public static void main(String[] args) {
List<String> s = Arrays.asList("foo");
List i = Arrays.asList(42); // warning about raw types
inspect( s, i); // warning about variadic generic parameter
}
}
|
Compliant Solution (Generic Argument)
All such noncompliant examples can be mitigated by avoiding raw types. Simply replacing the List
of integers with a List<Integer>
causes the compiler to refuse to compile each noncompliant code example.
Code Block | ||||
---|---|---|---|---|
| ||||
class ListInspector {
public static void inspect(List<String>... l) {
for (List<String> list : l) {
for (String s : list) { // ClassCastException on 42
System.out.println(s);
}
}
}
public static void main(String[] args) {
List<String> s = Arrays.asList("foo");
List<Integer> i = Arrays.asList(42);
inspect( s, i); // improper argument types compiler error
}
}
|
Risk Assessment
Mixing generic and nongeneric code can produce unexpected results and exceptional conditions.
...