Seiter's Blog

Mind of the machine

Java Purity and Version 7

, , , ,

With Java 7 coming out in just a hair over two weeks, I thought I'd take some time to look into the language features that were being added. The main items of interest from a language perspective are:

  1. Strings in switch statements
  2. Type inference for generics (Diamond)
  3. Underscores in numbers
  4. Try with resources

Strings in switch statements is fine and arguably should have been in Java a long time ago. You can now:

    public static void switchOnString( String string )
    {
        switch( string )
        {
            case "kevin":
                System.out.println( "In Kevin" );
                // fall through
            case "rob":
                System.out.println( "In Rob" );
                break;
            default:
                System.out.println( "In Default" );
        }
    }

Type inference for generics is a pretty-print enhancement. It makes otherwise verbose generics half as much so.

// before
Map<Integer,String> myMap = new HashMap<Integer,String>();

// after
Map<Integer,String> myMap = new HashMap<>();

Unfortunately, there is no support for initializing using Diamond when the types have to be inferred.

// Diamond lets us do this cleanly
Map<Integer,Map<Integer,String>> myMap = new HashMap<>();

// sadly, we can't do this, the new HashMap has to be fully qualified
myMap.put(new Integer(5), new HashMap<>());

Underscores in numbers is another aesthetic change, allowing erasure of underscores from numbers so that formatting can exist in the code.

long phoneNumber = 555_555_1212L;
double someCash = 12_526_123.45;

Finally, the try-with-resources. Now this one is actually something interesting. It basically allows the programmer to delegate the responsibility of closing resources to Java. Closing resources has always been kind of a pain, requiring try blocks inside finally blocks and so forth. Here's the flavor of the new syntax.

try(
   BufferedReader br = new BufferedReader(new FileReader(new File(fileName)))
){
// do something
}
catch(Exception ex)
{
// catch some exception as a consequence of doing something
}

Any time you add a language enhancement like this, however, you obscure some of the purity of the language. You lose a bit of the "what you see is what you get" feel. For example, if you declare more than one resource in the try, in what order are they processed? Let's find out. We'll build a quick object that conforms to the new AutoCloseable interface.

public class MyAutoCloseable implements AutoCloseable
{
    private String name;
    
    public MyAutoCloseable( String name )
    {
        this.name = name;
    }
    
    public void close()
    {
        System.out.println( "Closing:" + name );
    }
}

Now let's write a trivial case to test the ordering.

    try( 
        MyAutoCloseable mac1 = new MyAutoCloseable( "1" );
        MyAutoCloseable mac2 = new MyAutoCloseable( "2" );
        MyAutoCloseable mac3 = new MyAutoCloseable( "3" );
    )
        {
            System.out.println( "Do something..." );
        }
        catch( Exception ex )
        {
            System.out.println( "Exception:" + ex.getMessage() );
        }

This results in predictable, stack based resolution of the resources.

Do something...
Closing:3
Closing:2
Closing:1

But what if one of these close operations excepts? Will it continue to properly close the other two resources? If so, what if two of the closes except? Will you be able to see both exceptions? It's simple to find out by modifying our class.

public void close()
    {
        System.out.println( "Closing:" + name );
        if( name.equals("2") ) throw new NullPointerException("Closing resource two is exploding!");
        if( name.equals("3") ) throw new NullPointerException("Closing resource three is exploding!");
    }

Running this new condition results in very interesting output indeed.

Do something...
Closing:3
Closing:2
Closing:1
Exception:Closing resource three is exploding!

So the good news is that java is smart enough to make sure the resources have close() invoked regardless of whether or not other resources fail. The bad news is that you can lose exception information. The stack tied with this exception doesn't contain the second exception in the tree either. (As it shouldn't) This could actually happen with the old syntax as well but this syntax obfuscates the issue.

Moral of the story, new language features are great. Use them to your advantage as much as possible. But never forget that your responsibility as a competent developer is not only to make things work but to understand how they work.

Models and The Scientific MethodClojure!

Comments

Unregistered user Thursday, July 14, 2011 5:58:27 PM

steve writes: I pretty much don't understand your comment about loosing exception information. Isn't that exactly what getSuppressed() does? printStackTrace() also mentions the suppressed exception, even with nice indentation. Am I missing something?

Kevin SeiterSeiter Thursday, July 14, 2011 6:09:05 PM

I wasn't very verbose on that mostly because I didn't want to drag the post too far into the details. Here is an example of what I was talking about. Modify the close() method in this way:

public void close()
{
System.out.println( "Closing:" + name );
if( name.equals("2") ) throw new NullPointerException("Closing resource two is exploding!");
if( name.equals("3") ) throw new RuntimeException("Closing resource three is exploding!");
}

Now update your original try block to be more specific:

try(
MyAutoCloseable mac1 = new MyAutoCloseable( "1" );
MyAutoCloseable mac2 = new MyAutoCloseable( "2" );
MyAutoCloseable mac3 = new MyAutoCloseable( "3" );
)
{
System.out.println( "Do something..." );
}
catch( NullPointerException ex )
{
System.out.println( "Null pointer exception:" + ex.getMessage() );
}
catch( RuntimeException ex2 )
{
System.out.println( "Runtime exception:" + ex2.getMessage() );
}
catch( Exception ex3 )
{
System.out.println( "Generic exception:" + ex3.getMessage() );
}

Re-running in this way causes output of form:

Do something...
Closing:3
Closing:2
Closing:1
Null pointer exception:Closing resource three is exploding!

Note how, while the Runtime exception is in fact generated, the calling try never has his runtime exception catch block invoked.

Write a comment

New comments have been disabled for this post.

June 2012
S M T W T F S
May 2012July 2012
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30