Java 101: The Next Generation

Java 101: The essential Java language features tour, Part 5

Four more small-change language features introduced in Project Coin

Java 101: The Next Generation

Show More
1 2 Page 2
Page 2 of 2

Type inference and generic constructors for generic and non-generic classes

Generic and non-generic classes can declare generic constructors in which a constructor has a formal type parameter list. For example, you could declare the following generic class with a generic constructor:


public class Box<E>
{
   public <T> Box(T t)
   {
      // ...
   }
}

This declaration specifies generic class Box<E> with formal type parameter E. It also specifies a generic constructor with formal type parameter T. You could instantiate the generic class and invoke its constructor as follows:


new Box<Marble>("Aggies")

This expression creates an instance of Box<Marble>, passing Marble to E. Also, the compiler infers String as T's actual type argument because the constructor's argument is a String object.

Pre-Java 7 compilers infer a generic constructor's actual type arguments similarly to those of a generic method. However, Java 7's compiler can infer the actual type arguments of the generic class being instantiated in a diamond operator context. Consider the following example:


Box<Marble> box = new Box<>("Aggies");

As well as inferring the type Marble for formal type parameter E of generic class Box<E>, the compiler infers type String for formal type parameter T of this generic class's constructor.

Project Coin small change #8: Simplified varargs method invocation

Before Java 7, each attempt to invoke a varargs (variable arguments, also known as variable arity) method with a non-reifiable varargs type caused the compiler to output an "unsafe operation" warning. To eliminate the potential for many similar warning messages (one per call site), Java 7 moved the warning from the call site to the method declaration.

Generic methods that include vararg input parameters can cause heap pollution, in which a variable of a parameterized type refers to an object that isn't of that parameterized type (for instance if a raw type has been mixed with a parameterized type). The compiler reports an "unchecked warning" because the correctness of an operation involving a parameterized type (like a cast or method call) cannot be verified.

Listing 13 demonstrates heap pollution in a non-varargs context.

Listing 13. Demonstrating heap pollution in a non-varargs context


import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

public class HeapPollutionDemo
{
   public static void main(String[] args)
   {
      Set s = new TreeSet<Integer>();
      Set<String> ss = s;            // unchecked warning
      s.add(new Integer(42));        // another unchecked warning
      Iterator<String> iter = ss.iterator();
      while (iter.hasNext())
      {
         String str = iter.next();   // ClassCastException thrown
         System.out.println(str);
      }
   }
}

Variable ss has parameterized type Set<String>. When the java.util.Set that's referenced by s is assigned to ss, the compiler generates an unchecked warning. It does so because the compiler cannot determine that s refers to a Set<String> type (it does not). The result is heap pollution. (The compiler allows this assignment to preserve backwards compatibility with legacy Java versions that don't support generics. Furthermore, type erasure transforms Set<String> into Set, which results in one Set being assigned to another Set.)

The compiler generates a second unchecked warning on the line that invokes Set's add() method. It does so because it cannot determine if variable s refers to a Set<String> or Set<Integer> type. This is another heap pollution situation. (The compiler allows this method call because erasure transforms Set's boolean add(E e) method to boolean add(Object o), which can add any kind of object to the set, including the java.lang.Integer subtype of java.lang.Object.)

Heap pollution can easily occur in a varargs context. For example, consider Listing 14.

Listing 14. Demonstrating heap pollution in a varargs context


import java.util.Arrays;
import java.util.List;

public class UnsafeVarargsDemo
{
   public static void main(String[] args)
   {
      unsafe(Arrays.asList("A", "B", "C"),
             Arrays.asList("D", "E", "F"));
   }

   static void unsafe(List<String>... l)
   {
      Object[] oArray = l;
      oArray[0] = Arrays.asList(new Double(3.5));
      String s = l[0].get(0);
   }
}

The Object[] oArray = l; assignment introduces the possibility of heap pollution. A value not matching the parameterized type of the varargs parameter l can be assigned to variable oArray. However, the compiler doesn't generate an unchecked warning because it has already done so when translating List<String>... l to List[] l. This assignment is valid because variable l has the type List[], which subtypes Object[].

Also, the compiler doesn't issue a warning or error when assigning a List object of any type to any of oArray's array components; for example, oArray[0] = Arrays.asList(new Double(3.5));. This assignment assigns to the first array component of oArray a List object containing a single java.lang.Double object.

The String s = l[0].get(0); assignment is problematic. The object stored in the first array component of variable l has the type List<Double>, but this assignment expects an object of type List<String>. As a result, the JVM throws java.lang.ClassCastException.

Compile this source code (javac -Xlint:unchecked UnsafeVarargsDemo.java). You should observe the following output (slightly reformatted for readability) when compiled under Java SE 7 update 6:


UnsafeVarargsDemo.java:8: warning: [unchecked] unchecked generic array
creation for varargs parameter of
type List<String>[]
      unsafe(Arrays.asList("A", "B", "C"),
            ^
UnsafeVarargsDemo.java:12: warning: [unchecked] Possible heap pollution from
parameterized vararg type
List
   static void unsafe(List<String>... l)
                                      ^
2 warnings

In my Java 101 introduction to generics I stated that you cannot use type parameters in array-creation expressions. For example, you cannot specify elements = new E[size];. The compiler reports a "generic array creation error" message when you try to do so. However, it's still possible to create a generic array, but only in a varargs context, and that is what the first warning message is reporting. Behind the scenes, the compiler transforms List<String>... l to List<String>[] l and then to List[] l.

Notice that the heap pollution warning is generated at the unsafe() method's declaration site. This message isn't generated at this method's call site, which is the case with Java 5 and 6 compilers.

Not all varargs methods will contribute to heap pollution. However, a warning message will still be issued at the method's declaration site. If you know that your method doesn't contribute to heap pollution, you can suppress this warning by declaring it with the @SafeVarargs annotation -- Java 7 introduced the java.lang.SafeVarargs annotation type. For example, because there's no way for the Arrays class's asList() method to contribute to heap pollution, this method's declaration has been annotated with @SafeVarargs, as follows:


@SafeVarargs
public static <T> List<T> asList(T... a)

The @SafeVarargs annotation eliminates the generic array creation and heap pollution warning messages. It's a documented part of the method's contract and asserts that the method's implementation will not improperly handle the varargs formal parameter.

In conclusion

Java 7 improved developer productivity by introducing automatic resource management via the try-with-resources statement along with a new AutoCloseable interface, switch-on-string, multi-catch, final rethrow, binary literals, underscores in numeric literals, changes to the compiler's type inference algorithm that introduced the so-called diamond operator, and simplified varargs method invocation. Next up in the Java 101: The next generation series is a look at Java 8's lambda and functional interface language features.

1 2 Page 2
Page 2 of 2