Java 7 – Project Coin Feature Overview

We discussed previously everything that didn’t make it into Java 7 and then reviewed the useful Fork/Join Framework that did make it in. Today’s post will take us through each of the Project Coin features – a collection of small language enhancements that aren’t groundbreaking, but are nonetheless useful for any developer able to use JDK 7.

I’ve come up with a bank account class that showcases the basics of Project Coin features. Take a look…

public class ProjectCoinBanker {
  
  private static final Integer ONE_MILLION = 1_000_000;
  private static final String RICH_MSG = "You need more than $%,d to be considered rich.";

  public static void main(String[] args) throws Exception {
	System.out.println(String.format(RICH_MSG, ONE_MILLION));

	String requestType = args[0];
	String accountId = args[1];
	switch (requestType) {
		case "displayBalance": 
			printBalance(accountId); 
			break;
		case "lastActivityDate" : 
			printLastActivityDate(accountId); 
			break;
		case "amIRich" : 
			amIRich(accountId); 
			break;
		case "lastTransactions" : 
			printLastTransactions(accountId, Integer.parseInt(args[2])); 
			break;
		case "averageDailyBalance" : 
			printAverageDailyBalance(accountId); 
			break;
		default: break;
	}
  }
  
  private static void printAverageDailyBalance(String accountId) {
        String sql = String.format(AVERAGE_DAILY_BALANCE_QUERY, accountId);
	try (
	      PreparedStatement s = _conn.prepareStatement(sql);
              ResultSet rs = s.executeQuery();
             ) {
	        while (rs.next()) {
		  //print the average daily balance results
                }
	     } catch (SQLException e) {
		// handle exception, but no need for finally to close resources
                for (Throwable t : e.getSuppressed()) {
		   System.out.println("Suppressed exception message is " + t.getMessage());
		}
	     }
  }
  
  private static void printLastTransactions(String accountId, int numberOfTransactions) {
	List<Transaction> transactions = new ArrayList<>();
	... handle fetching/printing transactions
  }
  
  private static void printBalance(String accountId) {
	try {
		BigDecimal balance = getBalance(accountId);
		//print balance
	} catch (AccountFrozenException | ComplianceViolationException | AccountClosedException e) {
	    System.err.println("Please see your local branch for help with your account.");
	}
  }
  
  private static void amIRich(String accountId) {
	try {
		BigDecimal balance = getBalance(accountId);
		//find out if the account holder is rich
	} catch (AccountFrozenException | ComplianceViolationException | AccountClosedException e) {
	    System.out.println("Please see your local branch for help with your account.");
	}
  }
  
  private static BigDecimal getBalance(String acccountId) 
      throws AccountFrozenException, AccountClosedException, ComplianceViolationException {
      ... getBalance functionality
  }
 
}

Briefly, our ProjectCoinBanker class demonstrates basic usage of the following Project Coin features.

  • Underscores in numeric literals
  • Strings in switch
  • Multi-catch
  • Type inference for typed object creation
  • try with resources and suppressed exceptions

First of all, underscores in numeric literals are pretty self-explanatory. Our example, private static final Integer ONE_MILLION = 1_000_000; shows that the benefit is visual. Developers can quickly look at the code to verify that values are as expected. Underscores can be used in places other than natural groupings locations, being ignored wherever they are placed. Underscores in numeric literals cannot begin or terminate a numeric literal, otherwise, you’re free to place them where you’d like. While not demonstrated here, binary literal support has also been added. In the same way that hex literals are prefixed by 0x or 0X, binary literals would be prefixed by 0b or 0B.

Strings in switch are also self-explanatory. The switch statement now also accepts String. In our example, we switch on String argument passed to the main method to determine what request was made. On a side note, this is purely a compiler implementation with an indication that JVM support for switching on String may be added at a later date.

Type inference is another easy-to-understand improvement. Now instead of our old code List<Transaction> transactions = new ArrayList<Transaction>(); we can simply do List<Transaction> transactions = new ArrayList<>(); since the type can be inferred. Probably won’t find anyone who would argue that it shouldn’t have been so since the introduction of generics, but it’s now here.

Multi-catch will turn out to be very nice for the conciseness of exception handling code. Too many times when wanting to actually do something based on the exception type thrown, until now we were forced to have multiple catch blocks all doing essentially the same thing. The new syntax is very clean and logical. Our example, catch (AccountFrozenException | ComplianceViolationException | AccountClosedException e) shows how easily it can be done.

Finally, the last demonstration of a Project Coin feature is the try with resources syntax and support for retrieving suppressed exceptions. A new interface has been introduced, AutoCloseable that has been applied to all the expected suspects including Input/OutputStreams, Readers/Writers, Channels, Sockets, Selectors and java.sql resources Statement, ResultSet and Connection. In my opinion, the syntax is not as natural as the multi-catch change was, not that I have an alternative in mind.

    try (
        PreparedStatement s = _conn.prepareStatement(sql);
        ResultSet rs = s.executeQuery();
        ) {
	    while (rs.next()) {
	      //print the average daily balance results
            }
	} catch (SQLException e) {
	    //handle exception, but no need for finally to close resources
            for (Throwable t : e.getSuppressed()) {
	        System.out.println("Suppressed exception message is " + t.getMessage());
	    }
	}

First we see that we can include multiple resources in try with resources – very nice. We can even reference previously declared resources in the same block as we did with our PreparedStatement. We still handle our exception, but we don’t need to have a finally block just to close the resources. Notice too that there is a new method getSuppressed() on Throwable. This allows us to access any Exceptions that were thrown in trying to “autoclose” the declared resources. There can be at most one suppressed exception per resource declared. Note: if the resource initialization throws an exception, it would be handled in your declared catch block.

That’s it. Nothing earth-shattering but some simple enhancements that we can all begin using without too much trouble. Project Coin also includes a feature regarding varargs and compiler warnings. Essentially, it boils down to a new annotation (@SafeVarargs) that can be applied at the method declaration to allow developers to remove @SuppressWarnings("varargs") from their consuming code. This has been applied on all the key suspects within the JDK, but the same annotation is available to you in any of your genericized varags methods.

The Project Coin feature set as it is described online is inconsistent at best. Hopefully this will give you a solid summary of what you can use from the proposal in JDK 7.

Java 7 – Fork/Join

While we lamented how feature-poor Java 7 turned out to be, one thing that made it that turns out to be a boon to high-performance concurrent development is the new Fork/Join framework. This framework is targeted at multi-processor systems (really almost all hardware today) in situations where a batch of work can broken into smaller recursive calls. But more than just that, it also uses a work-stealing algorithm where threads with no work can steal available work from other threads that are busy. What makes that so useful is that you can try to break down your work into fairly small, roughly equal pieces, but some pieces can take longer than others and you’ll still get good use of your processing resources.

The Fork/Join framework builds on the ExecutorService we discussed in our Concurrency series and the implementation is ForkJoinPool that can execute the ForkJoinTask which is usually implemented as a child of either RecursiveTask(returns data) or RecursiveAction(no returned data).

As is our custom, let’s use a sample task and some applicable code. Let’s assume you want to write a class that will calculate the size on disk of a given directory and all its children. And in our case we’ll just make the assumption we’re running some wicked fast SSDs so we can actually benefit from concurrent scans.

Here’s our code:

import java.util.concurrent.*;
import java.io.*;
import java.util.*;

public class DirectorySizer extends RecursiveTask<Long> {
  
  private List<File> mFiles;
  private boolean mAllFiles = true;
  
  public DirectorySizer(List<File> files) {
    mFiles = files;
    for (File file : files) {
      if (file.isDirectory()) {
        mAllFiles = false;
      }
    }
  }
  
  protected Long compute() {
    if (mFiles.size() <=4 && mAllFiles) {
      return computeLocal();
    } else {
      return forkAndJoin();
    }  
  }
  
  private Long computeLocal() {
    long length = 0;
    for (File file : mFiles) {
	  length += file.length();
    }
    return length;
  }
  
  private Long forkAndJoin() {
    List<File> dirsAndFiles = new ArrayList();
	for (File file : mFiles) {
      if (file.isFile()) {
        dirsAndFiles.add(file);
      } else {
        dirsAndFiles.addAll(Arrays.asList(file.listFiles()));
      }
    }
    int rightSize = dirsAndFiles.size() / 2;
    int leftSize = dirsAndFiles.size() - rightSize;
    List<File> leftList = dirsAndFiles.subList(0, leftSize);
    List<File> rightList= dirsAndFiles.subList(leftSize, leftSize+rightSize);
    DirectorySizer d1 = new DirectorySizer(leftList);
    d1.fork();
    DirectorySizer d2 = new DirectorySizer(rightList);
    return d2.compute() + d1.join();
  }
  
  public static void main(String[] args) throws Exception {
    List<File> files = Arrays.asList(new File(args[0]).listFiles());
    DirectorySizer sizer = new DirectorySizer(files);
    ForkJoinPool pool = new ForkJoinPool();
    Long size = pool.invoke(sizer);
    System.out.println(args[0] + " is " + size + " bytes ");
  }
}

Let's break down the usage of the Fork/Join framework. By extending RecursiveTask, all we really need to do is implement the compute method calculating the size when the amount of work is small enough to suit our needs and using a fork/join when the chunk of work is still too large. In our main method we get all the benefit of the framework simply by creating a new ForkJoinPool and passing our top-level instance to the invoke method.

While it's true the potential uses of this framework are limited and require a fairly narrow problem scope, it's nice to see continued advancement in Java in the concurrency space to build on all the goodies we got in Java 5. If you have any concurrency problems you'd like us to tackle in our blog, drop us line. We enjoy talking shop with fellow developers.