Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: fixed variadic code examples (can corrupt list from variadic arg)

...

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(list, 1) is used instead of addToList(list, 1.0). Great care must be taken to ensure type safety when generics are mixed with nongeneric code.

Wiki Markup
h2. Noncompliant Code Example

...

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
bgColor#ffcccc
langjava
public class ListInspectorExample {
  public static void inspect(List<List<String>> l) {
    for (List<String> list : l) {
      for (String s : list) {      // ClassCastException on 42
        System.out.println(s);
      }
    }
  }

 (Variadic Arguments)

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}} 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:bgColor=#ffcccc|lang=java}
class ListModifierExample {
  public static void main(String[] argsmodify(List<String>... l) {
    List<String>Object[] sobjectArray = Arrays.asList("foo")l;
    List i objectArray[1] = Arrays.asList(42);    // warningpollutes aboutlist, rawno typeswarning

    List<List<String>>for l(List<String> =list new: ArrayList<List<String>>(l); {
    l.add(s);
   for l.add(i);   (String string : list) {        // ClassCastException on 42
        // warning about unchecked convertionSystem.out.println(string);
      }
    inspect(l);}
  }
}

Noncompliant Code Example (Array of lists)

The problem persists if the function parameter is replaced with an array of lists

Code Block
bgColor#ffcccc
langjava
public class ListInspectorExample {
  public static void inspect(List<String>[] l) { 
  public static void main(String[] args) {
    List<String> s = Arrays.asList("foo", "bar");
    for (List<String> list : l) {s2 = Arrays.asList("baz", "quux");
      for modify(String s, : lists2); {      // ClassCastException on 42
        System.out.println(s);
     // }
unchecked varargs warning
  }
  }
{code}

This program publicproduces staticthe 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
  }
}

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 Block
bgColor#ffcccc
langjava
public class ListInspectorExamplefollowing output:

{code}
foo
bar
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
	at ListModifierExample.modify(Java.java:13)
	at ListModifierExample.main(Java.java:25)
	at Java.main(Java.java:33)
{code}


h2. Noncompliant Code Example (Array of lists of strings)

This noncompliant code example is similar, but it uses an explicit array of lists of strings as the single parameter to {{modify()}}. The program again dies with a {{ClassCastException}} from the integer 42 injected into a list of strings.

class ListModifierExample {
  public static void inspectmodify(List<String>...[] l) {
    for (List<String> list : l) {
Object[] objectArray = l;       for (String s : list) {    // Valid
    objectArray[1] = Arrays.asList(42);   // pollutes ClassCastExceptionlist, onno 42warning

    for (List<String> list  System.out.println(s);: l) {
      }
for (String string : }
list) { }

         // ClassCastException on 42
        System.out.println(string);
      }
    }
  }
 
  public static void main(String[] args) {
    List<String> s = Arrays.asList("foo", "bar");
    ListList<String> is2 = Arrays.asList(42"baz", "quux");
    // warning about raw typesList l[] = {s, s2};
    inspect( s, i);modify(l);                            // warningunchecked about variadic generic parameterconversion warning
  }
}

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
bgColor#ccccff
langjava
public class ListInspectorExample {
  public static void inspect(List<String>... l) {
    for (List<String> list : l) {
      for (String s : list) {       // ClassCastException on 42
        System.out.println(s{code}


h2. Compliant Solution (List of lists of strings)

This compliant solution uses a list of lists of strings as the argument to {{modify()}}. This type safety enables the compiler to prevent the {{modify()}} method from injecting an integer into the list. In order to compile, the {{modify()}} method instead inserts a string, preventing heap pollution.

{code:bgColor=#ccccff|lang=java}
class ListModifierExample {
  public static void modify(List<List<String>> l) {
    l.set( 1, Arrays.asList("forty-two")); // no warning

    for (List<String> list : l) {
      for (String string : list) {        // 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>> l }
= new ArrayList<List<String>>();
  }
  }
l.add(s);
  public static void main(String[] args) { l.add(s2);
    List<String> s = Arrays.asList("foo");
    List<Integer> i = modify(l);
  }
}
{code}

Note that to avoid warnings, we cannot use {{Arrays.asList(42);
}} to build a inspect(list s, i);                // improper argument types compiler error
  }
}of list of strings, because that method is also variadic and would produce a warning about variadic arguments being parameterized class objects.

Risk Assessment

Mixing generic and nongeneric code can produce unexpected results and exceptional conditions.

...