Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: added variadic argument NCCE/CS

...

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

Noncompliant Code Example (List of lists)

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

  public static void main(String[] args) {
    List<String> s = Arrays.asList("foo");
    List i = Arrays.asList(42);    // warning about raw types
    List<List<String>> l = new ArrayList<List<String>>();
    l.add(s);
    l.add(i);                      // warning about unchecked convertion
    inspect(l);
  }
}

h2. Noncompliant Code Example (Array of lists)

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

{code:bgColor=#ffcccc|lang=java}
  public static void inspect(List<String>[] l) {
    for (List<String> list : l) {
      for (String s : list) {      // ClassCastException on 42
        System.out.println(s);
      }
    }
  }

  public static 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
  }
}


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

  public static void main(String[] args) {
    List<String> s = Arrays.asList("foo");
    List i = Arrays.asList(42);    // warning about raw types
    inspect( s, i);                // warning about variadic generic parameter
  }
}

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

  public static void main(String[] args) {
    List<String> s = Arrays.asList("foo");
    List<Integer> i = Arrays.asList(42);
    inspect( s, i);                // improper argument types compiler error
  }
}

Risk Assessment

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

...