This page covers the following topics:
Most parameterized types, such as
ArrayList<Number>
and
List<String>
, are non-reifiable types.
A non-reifiable type is a type that is not completely
available at runtime. At compile time, non-reifiable types undergo
a process called type erasure during which the compiler removes
information related to type parameters and type arguments. This
ensures binary compatibility with Java libraries and applications
that were created before generics. Because type erasure removes
information from parameterized types at compile-time, these types
are non-reifiable.
Heap pollution occurs when a variable of a parameterized type refers to an object that is not of that parameterized type. This situation can only occur if the program performed some operation that would give rise to an unchecked warning at compile-time. An unchecked warning is generated if, either at compile-time (within the limits of the compile-time type checking rules) or at runtime, the correctness of an operation involving a parameterized type (for example, a cast or method call) cannot be verified.
Consider the following example:
List l = new ArrayList<Number>(); List<String> ls = l; // unchecked warning l.add(0, new Integer(42)); // another unchecked warning String s = ls.get(0); // ClassCastException is thrown
During type erasure, the types
ArrayList<Number>
and
List<String>
become ArrayList
and
List
, respectively.
The variable ls
has the parameterized type
List<String>
. When the List
referenced by l
is assigned to ls
, the
compiler generates an unchecked warning; the compiler is unable to
determine at compile time, and moreover knows that the JVM will not
be able to determine at runtime, if l
refers to a
List<String>
type; it does not. Consequently,
heap pollution occurs.
As a result, at compile time, the compiler generates another
unchecked warning at the add
statement. The compiler
is unable to determine if the variable l
refers to a
List<String>
type or a
List<Integer>
type (and another heap pollution
situation occurs). However, the compiler does not generate a
warning or error at the get
statement. This statement
is valid; it is calling the List<String>.get
method to retrieve a String
object. Instead, at
runtime, the get
statement throws a
ClassCastException
.
In detail, a heap pollution situation occurs when the
List
object l
, whose static type is
List<Number>
, is assigned to another
List
object, ls
, that has a different
static type, List<String>
. However, the compiler
still allows this assignment. It must allow this assignment to
preserve backwards compatibility with versions of Java SE that do
not support generics. Because of type erasure,
List<Number>
and List<String>
both become List
. Consequently, the compiler allows
the assignment of the object l
, which has a raw type
of List
, to the object ls
.
Furthermore, a heap pollution situation occurs when the
l.add
method is called. The static type second formal
parameter of the add
method is String
,
but this method is called with an actual parameter of a different
type, Integer
. However, the compiler still allows this
method call. Because of type erasure, the type of the second formal
parameter of the add
method (which is defined as
List<E>.add(int,E)
) becomes Object
.
Consequently, the compiler allows this method call because, after
type erasure, the l.add
method can add any object of
type Object
, including an object of
Integer
type, which is a subtype of
Object
.
Consider the method ArrayBuilder.addToList
in the
following example. It is a variable arguments (also known as
varargs) method that adds the objects of type T
contained in the elements
varargs formal parameter to
the List
listArg
:
import java.util.*; public class ArrayBuilder { public static <T> void addToList (List<T> listArg, T... elements) { for (T x : elements) { listArg.add(x); } } public static void faultyMethod(List<String>... l) { Object[] objectArray = l; // Valid objectArray[0] = Arrays.asList(new Integer(42)); String s = l[0].get(0); // ClassCastException thrown here } }
import java.util.*; public class HeapPollutionExample { public static void main(String[] args) { List<String> stringListA = new ArrayList<String>(); List<String> stringListB = new ArrayList<String>(); ArrayBuilder.addToList(stringListA, "Seven", "Eight", "Nine"); ArrayBuilder.addToList(stringListA, "Ten", "Eleven", "Twelve"); List<List<String>> listOfStringLists = new ArrayList<List<String>>(); ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB); ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!")); } }
The Java SE 7 compiler generates the following warning for the
definition of the method ArrayBuilder.addToList
:
warning: [varargs] Possible heap pollution from parameterized vararg type T
When the compiler encounters a varargs method, it translates the
varargs formal parameter into an array. However, the Java
programming language does not permit the creation of arrays of
parameterized types. In the method
ArrayBuilder.addToList
, the compiler translates the
varargs formal parameter T... elements
to the formal
parameter T[] elements
, an array. However, because of
type erasure, the compiler converts the varargs formal parameter to
Object[] elements
. Consequently, there is a
possibility of heap pollution. See the next section, Potential Vulnerabilities of Varargs Methods
with Non-Reifiable Formal Parameters, for more information.
Note: The Java SE 5 and 6 compilers generate
this warning when the ArrayBuilder.addToList
is
called; in this example, the warning is generated for the class
HeapPollutionExample
. These compilers do not generate
the warning at the declaration site. However, the Java SE 7
generates the warning at both the declaration site and the call
site (unless the warnings are preempted with annotations; see
Suppressing Warnings from Varargs Methods
with Non-Reifiable Formal Parameters for more information). The
advantage of generating a warning when a compiler encounters a
varargs method that has a non-reifiable varargs formal parameter at
the declaration site as opposed to the call site is that there is
only one declaration site; there are potentially many call
sites.
The method ArrayBuilder.faultyMethod
shows why the
compiler warns you about these kinds of methods. The first
statement of this method assigns the varargs formal parameter
l
to the Object
array
objectArgs
:
Object[] objectArray = l;
This statement can potentially introduce heap pollution. A value
that does match the parameterized type of the varargs formal
parameter l
can be assigned to the variable
objectArray
, and thus can be assigned to
l
. However, the compiler does not generate an
unchecked warning at this statement. The compiler has already
generated a warning when it translated the varargs formal parameter
List<String>... l
to the formal parameter
List[] l
. This statement is valid; the variable
l
has the type List[]
, which is a subtype
of Object[]
.
Consequently, the compiler does not issue a warning or error if
you assign a List
object of any type to any array
component of the objectArray
array as shown by this
statement:
objectArray[0] = Arrays.asList(new Integer(42));
This statement assigns to the first array component of the
objectArray
array with a List
object that
contains one object of type Integer
.
Suppose you call the ArrayBuilder.makeArray
method
with the following statement:
ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));
At runtime, the JVM throws a ClassCastException
at
the following statement:
String s = l[0].get(0); // ClassCastException thrown here
The object stored in the first array component of the variable
l
has the type List<Integer>
, but
this statement is expecting an object of type
List<String>
.
If you declare a varargs method that has parameterized
parameters, and you ensure that the body of the method does not
throw a ClassCastException
or other similar exception
due to improper handling of the varargs formal parameter (as shown
in the ArrayBuilder.faultyMethod
method), you can
suppress the warning that the compiler generates for these kinds of
varargs methods by using one of the following options:
Add the following annotation to static and non-constructor method declarations:
@SafeVarargs
Unlike the @SuppressWarnings
annotation, the
@SafeVarargs
annotation is a documented part of the
method's contract; this annotation asserts that the implementation
of the method will not improperly handle the varargs formal
parameter.
Add the following annotation to the method declaration:
@SuppressWarnings({"unchecked", "varargs"})
Unlike the @SafeVarargs
annotation, the
@SuppressWarnings("varargs")
does not suppress
warnings generated from the method's call site.
Use the compiler option -Xlint:varargs
.
For example, the following version of the
ArrayBuilder
class has two additional methods,
addToList2
and addToList3
:
public class ArrayBuilder { public static <T> void addToList (List<T> listArg, T... elements) { for (T x : elements) { listArg.add(x); } } @SuppressWarnings({"unchecked", "varargs"}) public static <T> void addToList2 (List<T> listArg, T... elements) { for (T x : elements) { listArg.add(x); } } @SafeVarargs public static <T> void addToList3 (List<T> listArg, T... elements) { for (T x : elements) { listArg.add(x); } } // ... }
public class HeapPollutionExample { // ... public static void main(String[] args) { // ... ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB); ArrayBuilder.addToList2(listOfStringLists, stringListA, stringListB); ArrayBuilder.addToList3(listOfStringLists, stringListA, stringListB); // ... } }
The Java compiler generates the following warnings for this example:
addToList
:
[unchecked] Possible heap
pollution from parameterized vararg type T
[unchecked] unchecked generic
array creation for varargs parameter of type
List<String>[]
addToList2
: When the method is
called (no warning is generated at the method's declaration):
[unchecked] unchecked generic array creation for varargs
parameter of type List<String>[]
addToList3
: No warnings are
generated either at the method's declaration or when it is
called.Note: In Java SE 5 and 6, it is the responsibility of the programmer who calls a varargs method that has a non-reifiable varargs formal parameter to determine whether heap pollution would occur. However, if this programmer did not write such a method, he or she cannot easily determine this. In Java SE 7, it is the responsibility of the programmer who writes these kinds of varargs methods to ensure that they properly handle the varargs formal parameter and ensure heap pollution does not occur.