Versions Compared

Key

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

Parameterized classes have expectations of the objects that they reference; they expect certain objects to match their paramaterized types. Heap pollution occurs when a parameterized class references an object that it expects to be of the parameterized type, but the object is of a different type. For more information on heap pollution, see the Java Language Specification, §4.12.2.1, "Heap Pollution," [JLS 2005]). For instance, consider the following code snippet.

Code Block
List llist = new ArrayList();
List<String> ls = llist; // Produces unchecked warning

...

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, 142);
    System.out.println(list.get(0));  // throws ClassCastException
  }
}

...

Code Block
bgColor#ccccff
class ListUtility {
  private static void addToList(List<String> list, String str) {
    list.add(str);     // No warning generated
  }

  public static void main(String[] args) {
    List<String> list = new ArrayList<String> ();
    addToList(list, "142");
    System.out.println(list.get(0));
  }
}

...

Code Block
bgColor#ccccff
class ListUtility {
  private static void addToList(List list, Object obj) {
    list.add(obj); // Unchecked warning, also throws ClassCastException
  }

  public static void main(String[] args) {
    List<String> list = new ArrayList<String> ();
    List<String> checkedList = Collections.checkedList(list, String.class);
    addToList(checkedList, 142);
    System.out.println(list.get(0));
  }
}

...

This noncompliant code example compiles and runs cleanly because it suppresses the unchecked warning produced by the raw List.add() method. The printOneprintNum() method intends to print the value 142, either as an int or as a double depending on the type of the variable type.

Code Block
bgColor#FFCCCC
class ListAdder {
  @SuppressWarnings("unchecked")
  private static void addToList(List list, Object obj) {
    list.add(obj);  // unchecked warning suppressed
  }

  private static <T> void printOneprintNum(T type) {
    if (!(type instanceof Integer || type instanceof Double)) {
      System.out.println("Cannot print in the supplied type");
    }
    List<T> list = new ArrayList<T>();
    addToList(list, 142);
    System.out.println(list.get(0));
  }

  public static void main(String[] args) {
    double d = 142;
    int i = 142;
    System.out.println(d);
    ListAdder.printOneprintNum(d);
    System.out.println(i);
    ListAdder.printOneprintNum(i);
  }
}

However, despite list being correctly parameterized, this method prints 1 42 and never 142.0 because the int value 1 42 is always added to list without being type checked. This code produces the following output:

Code Block
142.0
142
142
1
42

Compliant Solution (Parameterized Collection)

...

Code Block
bgColor#ccccff
class ListAdder {
  private static <T> void addToList(List<T> list, T t) {
    list.add(t);     // No warning generated
  }

  private static <T> void printOneprintNum(T type) {
    if (type instanceof Integer) {
      List<Integer> list = new ArrayList<Integer>();
      addToList(list, 142);
      System.out.println(list.get(0));
    }
    else if (type instanceof Double) {
      List<Double> list = new ArrayList<Double>();

      addToList(list, 142.0);  // will not compile with 142 instead of 142.0

      System.out.println(list.get(0));
    }
    else {
      System.out.println("Cannot print in the supplied type");
    }
  }

  public static void main(String[] args) {
    double d = 142;
    int i = 142;
    System.out.println(d);
    ListAdder.printOneprintNum(d);
    System.out.println(i);
    ListAdder.printOneprintNum(i);
  }
}

This code compiles cleanly and produces the correct output:

Code Block
142.0
142.0
142
142

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 printOneprintNum() can be used, but no warnings result if addToList(list, 142) is used instead of addToList(list, 142.0). Great care must be taken to ensure type safety when generics are mixed with nongeneric code.

...

Heap pollution can occur without using raw types such as java.util.List. This noncompliant code example builds a list of list of strings, before passing it to a modify method. Because this method is variadic, it casts l list into an array of lists of strings. But Java is incapable of representing the types of parameterized arrays. This allows the modify() method 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>.

Code Block
bgColor#ffcccc
langjava
class ListModifierExample {
  public static void modify(List<String>... llist) {
    Object[] objectArray = llist;
    objectArray[1] = Arrays.asList(42);   // pollutes list, no warning

    for (List<String> listls : llist) {
      for (String string : listls) {          // ClassCastException on 42
        System.out.println(string);
      }
    }
  }
 
  public static void main(String[] args) {
    List<String> s = Arrays.asList("foo", "bar");
    List<String> s2 = Arrays.asList("baz", "quux");
    modify( s, s2);                       // unchecked varargs warning
  }
}

...

Code Block
bgColor#ffcccc
langjava
class ListModifierExample {
  public static void modify(List<String>[] llist) {
    Object[] objectArray = llist;          // Valid
    objectArray[1] = Arrays.asList(42);   // pollutes list, no warning

    for (List<String> listls : llist) {
      for (String string : listls) {          // ClassCastException on 42
        System.out.println(string);
      }
    }
  }
 
  public static void main(String[] args) {
    List<String> s = Arrays.asList("foo", "bar");
    List<String> s2 = Arrays.asList("baz", "quux");
    List llist[] = {s, s2};
    modify(llist);                            // unchecked conversion warning
  }
}

...

Code Block
bgColor#ccccff
langjava
class ListModifierExample {
  public static void modify(List<List<String>> llist) {
    llist.set( 1, Arrays.asList("forty-two")); // no warning

    for (List<String> listls : llist) {
      for (String string : listls) {            // ClassCastException on 42
        System.out.println(string);
      }
    }
  }
 
  public static void main(String[] args) {
    List<String> s = Arrays.asList("foo", "bar");
    List<String> s2 = Arrays.asList("baz", "quux");
    List<List<String>> llist = new ArrayList<List<String>>();
    llist.add(s);
    llist.add(s2);
    modify(llist);
  }
}

Note that to avoid warnings, we cannot use Arrays.asList() to build a list of list of strings, because that method is also variadic and would produce a warning about variadic arguments being parameterized class objects.

...