Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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
bgColor#FFCCCC
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
bgColor#FFCCCC
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)

...

[Bloch 2008]

Item 23. Don't use raw types in new code

[Bloch 2007]

[Bloch 2005]

Puzzle 88. Raw Deal

[Darwin 2004]

8.3, Avoid Casting by Using Generics

[JavaGenerics 2004]

 

[JLS 2005]

Chapter 5, Conversions and Promotions

 

§4.8, Raw Types

 

§5.1.9, Unchecked Conversion

[Langer 2008]

Topic 3, Coping with Legacy

[Naftalin 2006]

Chapter 8, Effective Generics

[Naftalin 2006b]

Principle of Indecent Exposure

[Schildt 2007]

Create a checked collection

[Tutorials 2008]

Heap Pollution

 

...

      04. Rule 05: Object Orientation (OBJ)