Heap pollution occurs when a variable of a parameterized type references an object that is not of that parameterized type. Even when heap pollution occurs, the variable is guaranteed to refer to a subclass or subinterface of the declared type, but is not guaranteed to always refer to a subtype of its declared type. For more information on heap pollution, see the Java Language Specification, §4.12.2., "Variables of Reference Type," [JLS 2014]).
Heap pollution can occur if the program performed some operation involving a raw type that would give rise to a compile-time unchecked warning.
For example, consider the following code snippet.
Mixing generically typed code with raw typed code is one common source of heap pollution. Generic types were unavailable prior to Java 5, so popular interfaces such as the Java Collection Framework relied on raw types. Mixing generically typed code with raw typed code allowed developers to preserve compatibility between non-generic legacy code and newer generic code but also gave rise to heap pollution. Heap pollution can occur if the program performed some operation involving a raw type that would give rise to a compile-time unchecked warning.
Code Block |
---|
List list = |
Code Block |
List list = new ArrayList();
List<String> ls = list; // Produces unchecked warning
|
It is insufficient to rely on unchecked warnings alone to detect violations of this rule. According to the Java Language Specification, §4.12.2.1, When generic and nongeneric types are used together correctly, these warnings can be ignored; at other times, these warnings can denote potentially unsafe operations. Mixing generic and raw types is allowed provided that heap pollution does not occur. For example, consider the following code snippet. In some cases, t is possible that a compile-time unchecked warning will not be generated. According to the Java Language Specification, §4.12.2.1, "Heap Pollution," [JLS 2005]:
...
Heap pollution can also occur if the program aliases an array variable of non-reifiable element type through an array variable of a supertype which is either raw or non-generic.
Extending legacy classes and making the overriding methods generic fails because this is disallowed by the Java Language Specification.
Mixing generically typed code with raw typed code is one common source of heap pollution. Prior to Java 5, all code used raw types. Allowing mixing enabled developers to preserve compatibility between non-generic legacy code and newer generic code. Using raw types with generic code causes most Java compilers to issue "unchecked" warnings but still compile the code. When generic and nongeneric types are used together correctly, these warnings can be ignored; at other times, these warnings can denote potentially unsafe operations.
According to the Java Language Specification, §4.8, "Raw Types," [JLS 2005]:
The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of genericity into the Java programming language is strongly discouraged. It is possible that future versions of the Java programming language will disallow the use of raw types.
Mixing generic and raw types is allowed provided that heap pollution does not occur.
Noncompliant Code Example
This noncompliant code example compiles but produces an unchecked warning because the raw type of the List.add()
method is used (the list
parameter in the addToList()
method) rather than the parameterized type.
Code Block | ||
---|---|---|
| ||
class ListUtility {
private static void addToList(List list, Object obj) {
list.add(obj); // unchecked warning
}
public static void main(String[] args) {
List<String> list = new ArrayList<String> ();
addToList(list, 42);
System.out.println(list.get(0)); // throws ClassCastException
}
}
|
Noncompliant Code Example
This noncompliant code example compiles but results in heap pollution. The compiler produces an unchecked warning because a raw argument (the obj
parameter in the addToList()
method) is passed to the List.add()
method.
Code Block | ||
---|---|---|
| ||
class ListUtility {
private static void addToList(List list, Object obj) {
list.add(obj); // unchecked warning
}
public static void main(String[] args) {
List<String> list = new ArrayList<String> ();
addToList(list, 42);
System.out.println(list.get(0)); // throws ClassCastException
}
}
|
Heap pollution is possible in this case because the parameterized type information is discarded prior to execution. The call to addToList(list, 42)
succeeds in adding an integer to list
although it is of type List<String>
. This Java runtime does not throw a ClassCastException
until the value is read and has an invalid type (an int rather than a String
). In other words, the code throws an exception some time after the execution of the operation that actually caused the error, complicating debugging.
Even when heap pollution occurs, the variable is still guaranteed to refer to a subclass or subinterface of the declared type, but is not guaranteed to always refer to a subtype of its declared type. In this example, list
does not refer to a subtype of its declared type (List<String>)
, but only to the subinterface of the declared type (List
)When executed, this code throws an exception. This happens not because a List<String>
receives an Integer
but because the value returned by list.get(0)
is an improper type (an Integer
rather than a String
). In other words, the code throws an exception some time after the execution of the operation that actually caused the error, complicating debugging.
Compliant Solution (Parameterized Collection)
...
The compiler prevents insertion of an Object
to the parameterized list
because addToList()
cannot be called with an argument whose type produces a mismatch. This code has consequently been changed to add a String
to the list instead of an Integer
int
.
Compliant Solution (Legacy Code)
...
Item 23. Don't use raw types in new code | |
Puzzle 88. Raw Deal | |
8.3, Avoid Casting by Using Generics | |
| |
[JLS 2005] | |
| |
| |
Topic 3, Coping with Legacy | |
Chapter 8, Effective Generics | |
Principle of Indecent Exposure | |
Create a checked collection | |
Heap Pollution |
...