Java Purity and Version 7
Thursday, July 14, 2011 12:37:52 AM
- Strings in switch statements
- Type inference for generics (Diamond)
- Underscores in numbers
- 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.


Unregistered user # Thursday, July 14, 2011 5:58:27 PM
Kevin SeiterSeiter # Thursday, July 14, 2011 6:09:05 PM
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.