Java 9's other new enhancements, Part 2: Milling Project Coin

JEP 213 builds on Java 7's Project Coin with small language improvements and additions for your Java programs

A stack of coins
Credit: Randen Pederson/Flickr

JEP 213: Milling Project Coin defines a set of small language changes for Java 9. Three of these changes are related to the OpenJDK Project Coin-based language changes that debuted in Java 7, and the other two changes are considered Coin-like. This post introduces you to all of these tiny language improvements.

Smoothing Project Coin

JEP 213 broadens the scope of Java 7's SafeVarargs annotation type and diamond operator while enhancing the try-with-resources statement. This section explores all of these changes.

Allowing SafeVarargs to support private methods

Java 7 introduced the SafeVarargs annotation type for asserting that the bodies of annotated final or static methods, or constructors (which can be seen as a special kind of static method) do not perform potentially unsafe operations on their varargs (variable number of arguments) parameters. Java 9 expands this capability to also include private methods.

Applying @SafeVarargs to a static/final/private method header or constructor header suppresses unchecked warnings about a non-reifiable varargs type at call sites (locations where the method/constructor is called), and also suppresses unchecked warnings about parameterized array creation at call sites.

The following code fragment demonstrates an unsafe operation being performed on a method's varargs parameter, which receives a comma-delimited sequence of Lists containing String objects:

void badVarargsMethod(List<String>... ls)
{
   Object[] oa = ls;          // line 1
   oa[0] = Arrays.asList(22); // line 2
   String s = ls[0].get(0);   // line 3
}

Line 1 is legal because arrays are covariant (an array of supertype references is a supertype of an array of subtype references). Also, type erasure converts List<String>[] (the actual type of the List<String>... syntactic sugar) to the List runtime type, and List subtypes Object.

Line 2 is also legal. Because of type erasure, the JVM doesn't throw ArrayStoreException when it encounters oa[0] = Arrays.asList(22);, which boxes 22 into a Integer object and stores this object in the returned List before the assignment. After all, you're assigning a List reference to a List[] array at runtime.

Line 3 is problematic. A List<Integer> object has been stored in an array that can only hold List<String> objects. When the compiler-inserted cast operator attempts to cast ls[0].get(0)'s return value (an Integer object containing 22) to String, the cast operator throws a ClassCastException object.

Obviously, you wouldn't annotate badVarargsMethod()'s header @SafeVarargs because this method isn't safe. In contrast, you could annotate Listing 1's goodVarargsMethod() header @SafeVarargs because this method doesn't perform an unsafe operation.

Listing 1. SafeVarargsDemo.java (version 1)

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

public class SafeVarargsDemo
{
   public static void main(String[] args)
   {
      SafeVarargsDemo o = new SafeVarargsDemo();
      o.goodVarargsMethod(Arrays.asList("North", "South"),
                          Arrays.asList("Up", "Down"));
   }

   private void goodVarargsMethod(List<String>... ls)
   {
      for (List<String> los: ls)
         System.out.println(los);
   }
}

Compile Listing 1 as follows:

javac -Xlint:unchecked SafeVarargsDemo.java

You should observe the following warning messages (reformatted for readability):

SafeVarargsDemo.java:9: warning: [unchecked] unchecked generic array creation for varargs 
   parameter of type List<String>[] o.goodVarargsMethod(Arrays.asList("North", "South"),
                                                       ^
SafeVarargsDemo.java:13: warning: [unchecked] Possible heap pollution from parameterized 
   vararg type List<String> private void goodVarargsMethod(List<String>... ls)
                                                                           ^
2 warnings

The second warning message refers to heap pollution. Generic methods that include varargs 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).

You can make these warning messages go away by using @SafeVarargs, as shown in the following excerpt from a second version of the SafeVarargsDemo application:

@SafeVarargs
private void goodVarargsMethod(List<String>... ls)
{
   for (List<String> los: ls)
      System.out.println(los);
}

This time, the Java 9 compiler will report no warning messages. Note that you will receive an error message when attempting to compile the code in Java 8. The error message will tell you that the annotation isn't valid because the method isn't final. Java 9 doesn't have a problem because the method is private.

Allowing the diamond operator to be used with anonymous classes

Java 7 modified the compiler's type inference algorithm in a generic class instantiation context so that you can replace the actual type arguments used to invoke a generic class's constructor with an empty list (<>), provided that the compiler can infer the type arguments from the instantiation context. The <> syntax is commonly referred to as the diamond operator.

The following code fragment demonstrates how the diamond operator is used:

List<String> list1 = new ArrayList<String>();
List<String> list2 = new ArrayList<>();

The first line illustrates the pre-Java 7 approach to instantiating a generic class: the actual type argument must be specified when instantiating the generic class. Starting with Java 7, the duplicate type information is inferred from the surrounding context and doesn't need to be specified. Hence, the second line specifies <>.

However, you could not use the diamond operator with an anonymous class. For example, the Java 7 and Java 8 compilers would report an error if you specified Iterator<String> iter = new Iterator<>() { /* ... */ };, which attempts to instantiate an anonymous class that implements a generic interface.

JSR 334 has the following to say about using diamond with anonymous classes:

"Using diamond with anonymous inner classes is not supported since doing so in general would require extensions to the class file signature attribute to represent non-denotable types, a de facto JVM change."

Additional information is located in the Project Coin mailing list's Diamond operator and anonymous classes topic:

"Internally, a Java compiler operates over a richer set of types than those that can be written down explicitly in a Java program. The compiler-internal types which cannot be written in a Java program are called non-denotable types. Non-denotable types can occur as the result of the inference used by diamond. Therefore, using diamond with anonymous inner classes is not supported since doing so in general would require extensions to the class file signature attribute to represent non-denotable types, a de facto JVM change. It is feasible that future platform versions could allow use of diamond when creating an anonymous inner class as long as the inferred type was denotable."

Java 9 has updated its type inference algorithm to determine whether or not the inferred type is denotable when analyzing an anonymous class that leverages the diamond operator. Listing 2's contrived AnonDiamondDemo application's source code lets you observe this type inference change.

Listing 2. AnonDiamondDemo.java

import java.util.Iterator;
import java.util.NoSuchElementException;

public class AnonDiamondDemo
{
   public static void main(String[] args)
   {
      String[] directions = { "North", "South", "East", "West" };
      Iterator<String> iter = new Iterator<>()
                              {
                                 int i = 0;

                                 @Override
                                 public boolean hasNext()
                                 {
                                    return i < directions.length;
                                 }

                                 @Override
                                 public String next()
                                 {
                                    if (!hasNext())
                                       throw new NoSuchElementException();
                                    return directions[i++];
                                 }
                              };
      while (iter.hasNext())
         System.out.println(iter.next());
   }
}

Compile Listing 2 as follows:

javac AnonDiamondDemo.java

Run the resulting application as follows:

java AnonDiamondDemo

You should observe the following output:

North
South
East
West

Enhancing the try-with-resources statement

Java 7 introduced the try-with-resources statement, which requires a fresh variable to be declared for each resource that try-with-resources manages. Java 9 enhances try-with-resources as follows: if the resource is referenced by a final or effectively final variable, try-with-resources can manage the resource without a new variable being declared.

Consider the following pre-Java 9 try-with-resources code fragment:

FileInputStream fis = new FileInputStream("movie.mp4");
// ...

try (FileInputStream fis2 = fis) 
{
   // work with fis2
} 
catch (IOException e) 
{
  // ...
}

Note that a new local variable (fis2) is required so that the resource referenced by fis can be accessed. Unfortunately, fis cannot be accessed directly. This limitation is removed in Java 9, which is demonstrated in the following code fragment (note that fis is effectively final):

FileInputStream fis = new FileInputStream("movie.mp4");
// ...

try (fis) 
{
   // work with fis
} 
catch (IOException e) 
{
  // ...
}

Accommodating two coin-like changes

JEP 213 also reserves the _ character as a keyword and introduces support for private methods in interfaces. This section explores both of these changes.

Reserving the underscore character

The rich Java language offers many interesting items to discover. For example, you can use sequences of underscores as identifier names. Although this practice makes sense when the underscores serve as a temporary placeholder while you look for a more appropriate name, your source code can become hard to read, as demonstrated in Listing 3.

Listing 3. UnderscoreDemo.java

public class UnderscoreDemo
{
   public static void main(String[] __)
   {
      for (int ___ = 0; ___ < __.length; ___++)
         System.out.println(__[___]);
   }
}

Before Java 9, you could use a single underscore to name an identifier. However, the Java 8 compiler output a warning message when it discovered this practice. Java 9 is even more draconian in that its compiler outputs an error message. Why invalidate the single underscore?

Future versions of the Java language may reserve this name as a keyword and/or give it special semantics. In fact, JEP 302: Lambda Leftovers, which is being considered for Java 10, states that the underscore character is being rehabilitated to indicate unused lambda parameters. Here is the relevant JEP 302 excerpt:

"In many languages, it is common to use an underscore (_) to denote an unnamed lambda parameter (and similarly for method and exception parameters):
BiFunction<Integer, String, String> biss = (i, _) -> String.valueOf(i);

This [practice] allows stronger static checking of unused arguments, and also allows multiple arguments to be marked as unused. However, because underscore was a valid identifier as of Java 8, compatibility required us to take a more indirect path to getting to where underscore could serve this role in Java. Phase 1 was forbidding underscore as a lambda formal parameter name in Java 8 (this had no compatibility consequence, since lambdas did not exist previously) and a warning was issued for using underscore as an identifier in other places. Phase 2 came in Java 9, when this warning became an error. We are now free to complete the planned rehabilitation of underscore to indicate an unused lambda, method, or catch formal parameter."

1 2 Page 1