Versions Compared

Key

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

...

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.

Noncompliant Code Example (

...

Variadic Arguments)

Heap pollution occurs because Java does not enforce type correctness on lists of lists. This program happily tries to build can occur without using raw types such as java.util.List. This noncompliant code example builds a list of list of strings, while trying before passing it to sneak a single integer into the list. While the Java 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>, and throws a ClassCastException.

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

    for (List<String> list : l) {
      for (String sstring : list) {        // ClassCastException on 42
        System.out.println(sstring);
      }
    }
  }
 
  public static void main(String[] args) {
    List<String> s = Arrays.asList("foo", "bar");
    ListList<String> is2 = Arrays.asList(42);    // warning about raw types"baz", "quux");
    List<List<String>>modify( l = new ArrayList<List<String>>(s, s2);
    l.add(s);
    l.add(i);               // unchecked varargs     // warning about unchecked convertion
    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) {
    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
  }
}

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.

This program produces the following output:

Code Block
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)

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.

Code Block
bgColor#ffcccc
langjava
class ListModifierExample {
  public static void modify(List<String>[] l) {
    Object[] objectArray = l;       // Valid
    objectArray[1] = Arrays.asList(42);   // pollutes list, no warning
Code Block
bgColor#ffcccc
langjava
public class ListInspectorExample {
  public static void inspect(List<String>... l) {
    for (List<String> list : l) {
      for (String sstring : list) {        // ClassCastException on 42
        System.out.println(sstring);
      }
    }
  }
 
  public static void main(String[] args) {
    List<String> s = Arrays.asList("foo", "bar");
    List<String> s2 = Arrays.asList("foo("baz", "quux");
    List il[] = Arrays.asList(42 {s, s2};
    modify(l);    // warning about raw types
    inspect( s, i);                // warningunchecked about variadic generic parameterconversion warning
  }
}

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 pollutionAll 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 ListInspectorExampleListModifierExample {
  public static void inspect(List<String>... l) {modify(List<List<String>> l) {
    l.set( 1, Arrays.asList("forty-two")); // no warning

    for (List<String> list : l) {
      for (String sstring : list) {        // ClassCastException on 42
        System.out.println(sstring);
      }
    }
  }
 
  public static void main(String[] args) {
    List<String> s = Arrays.asList("foo", "bar");
    List<Integer>List<String> is2 = Arrays.asList(42);
    inspect( s, i);"baz", "quux");
    List<List<String>> l = new ArrayList<List<String>>();
    l.add(s);
    // improper argument types compiler errorl.add(s2);
    modify(l);
  }
}

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.

Risk Assessment

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

...