Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: general edit

...

Wiki Markup
Note that this does not imply that heap pollution only occurs if an unchecked warning actually occurred. It is possible to run a program where some of the binaries were compiled by a compiler for an older version of the Java programming language, or by a compiler that allows the unchecked warnings to suppressed _\[sic\]_. This practice is unhealthy at best.

Overriding Extending legacy classes and generifying the overriding method methods is not a panacea as this is made illegal by the Java Language Specification [JLS 05]. It is best to avoid mixing generic and non-generic code.

...

When executed, this code produces an exception because the value returned by list.get(0) is not of the proper type. , that is, String:

Code Block
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
	at Raw.print(Test.java:11)
	at Raw.main(Test.java:14)

...

By gleaning information from the diagnostic exception message, the error can be quickly traced to the line addToList(1) in the noncompliant code example. Changing this to addToList("1") is unfortunately a superficial defense. To resolve the real issue, use parameterized types must be used consistently and not just abundantly.

To enforce compile time checking of types, replace the parameters to the method addToList() with List<String> list and String str. The compiler does not allow insertion of an Object once list is parameterized. Likewise, addToList() cannot be called with an argument whose type produces a mismatch.

Code Block
bgColor#ccccff
class Parameterized {
  private static void addToList(List<String> list, String str) {
    list.add(str);     // Unchecked warning
  }
  private static void print() {
    List<String> list = new ArrayList<String> ();
    addToList(list, "1");
    System.out.println(list.get(0));
  }
  public static void main(String[] args) {
    Parameterized.print();
  }
}

The compiler does not allow insertion of an Object once list is parameterized. Likewise, addToList() cannot be called with an argument whose type produces a mismatch.

Noncompliant Code Example

This noncompliant code example suffers from related pitfalls. It compiles and runs cleanly. The method printOne() intends to print the value one, either as an int or as a double depending on the type of the variable type. However, despite list being correctly parameterized, this method always print '1' and never '1.0' because the int value '1' is always added to list without being type checked.

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

  private static <T> void printOne(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, 1);
    System.out.println(list.get(0));
  }

  public static void main(String[] args) {
    double d = 1;
    int i = 1;
    System.out.println(d); 
    BadListAdder.printOne(d);
    System.out.println(i);
    BadListAdder.printOne(i);
  }
}

However, despite list being correctly parameterized, this method prints '1' and never '1.0' because the int value '1' is always added to list without being type checked.

This code produces the output:

Code Block
1.0 
1  
1
1

Compliant Solution

If possible, This compliant solution generifies the addToList() method should be generified to eliminate possible type violations.

Code Block
bgColor#ccccff
class GoodListAdder {
  private static void addToList(List<Integer> list, Integer i) {
    list.add(i);
  }

  private static void addToList(List<Double> list, Double d) {
    list.add(d);
  }

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

      // This will not compile if addToList(list, 1) is used
      addToList(list, 1.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 = 1;
    int i = 1;
    System.out.println(d);
    GoodListAdder.printOne(d);
    System.out.println(i);
    GoodListAdder.printOne(i);
  }
}

...