A Lot to Explore
Java 7 - Project Coin - Part 2
Improved exception handling
There are two parts to this improvement – multi-catch and, effectively, final rethrow. To see why they’re helpful, consider the following Java 6 code, which tries to find, open, and parse a configuration file and handles a number of different possible exceptions, as shown in listing 1. getConfig() is a method that can encounter a number of different exceptional conditions:
• The configuration file may not exist.
• It may disappear while we’re trying to read from it.
• It may be malformed syntactically.
• It may have invalid information in it.
The exceptions really fit into two distinct functional groups. Either the file is missing or bad in some way or the file is present and correct in theory but was not retrievable (perhaps because of hardware failure or network outage). It would be nice to compress the cases down into just these two cases.
Java 7 allows us to do this, as shown in listing 2.
Note that the exception has to be handled in the catch block as the common supertype of the possible exceptions (which will usually be Exception or Throwable in practice) because the exact type is not knowable at compile-time.
An additional bit of new syntax is for helping with rethrowing exceptions. In many cases, developers may want to manipulate a thrown exception before rethrowing it. The problem is that, in previous versions of Java, code like this:
will force the programmer to declare the exception signature of this code as Throwable – the real dynamic type of the exception has been swallowed. However, it’s relatively easy to see that the exception can only be an IOException or a SQLException and, if we can see it, then so can the compiler. In this snippet, we’ve made a single word change to use the next Java 7 syntax:
The appearance of the final keyword indicates that the type that is actually thrown is the runtime type of the exception that was actually encountered – in this example, this would either be IOException or SQLException. This is referred to as “final rethrow” and can protect against throwing an overly general type here, which then has to be caught by a very general catch in a higher scope. Enhancements in the compiler mean that the final keyword is actually optional, but we’ve found that, while starting out with this feature, it’s actually easier to include it.
In addition to these general improvements to exception handling, the specific case of handling resources has been improved in 7 – so that’s where we’ll turn next.
This change is easy to explain but has proven to have hidden subtleties, which made it much less easy to implement than originally hoped. The basic idea is to allow a resource (for example, a file or something a bit like one) to be scoped to a block in such a way that the resource is automatically closed when control exits the block.
This is an important change for the simple reason that virtually no one gets the manual handling of resource closing 100 percent right. Until recently, even the reference how-to from Sun was wrong. The proposal submitted to Project Coin for this change includes the astounding claim that two thirds of the uses of close() in the JDK had bugs in them!
Fortunately, compilers can be made to excel at producing exactly the sort of pedantic, boilerplate code that humans so often get wrong, and that’s the approach taken by this change, which is usually referred to as try-with-resources.
This is a big help in writing error-free code. To see just how much, consider how you would write a block of code in order to read from a URL-based stream URL and write to a file with Java 6. It would look something like it’s shown in listing 3.
The key point here is that, when handling external resources, Murphy’s Law applies – anything can go wrong at any time:
1. The InputStream can fail:
• To open from the URL.
• To read from it.
• To close properly.
2. The file corresponding to the OutputStream can fail:
• To open.
• To write to it.
• To close properly.
Or have some combination of more than one of the above.
This last possibility is actually where a lot of the headaches come from – the possibility of some combination of exceptions is very difficult to deal with well.
Let’s consider some Java 7 code for saving code from the web. As the name suggests, url is a URL object that points at the entity we want to download, and file is a File object where we want to save what we’re downloading. Let’s look at Listing 4.
This basic form shows the new syntax for a block with automatic management – the try with the resource in round brackets. For C# programmers, this is probably a bit reminiscent of a using clause and that’s a good starting point when working with this new feature. The resources are used by the block and then automatically disposed of when you’re done with them. You still need to worry about handling exceptions with regards to finding the valid resource in the first place, but, once you’re using it, the resource gets automatically closed.
This is the main reason for preferring the new syntax – it’s just much less error prone – the compiler is not susceptible to the mistakes that basically every developer will make when trying to write this type of code manually.
One of the problems with generics is that the definitions and the setup of instances can be really verbose. Let’s suppose that you have some users, whom you identify by a user id (which is an integer), and each user has some lookup tables, and the tables are specific to each user. What would that look like in code?
That’s quite a mouthful, and almost half of it is just duplicated characters. Wouldn’t it be better if we could just write something like the code below, and have the compiler just infer the type information on the right hand side?
Map<Integer, List<String, String>> usersLists = new HashMap<>();
Thanks to the magic of Project Coin – you can. In Java 7, the shortened form for declarations like that is entirely legal. It’s backwards compatible as well so, when you find yourself revisiting old code, you can just cut the older, more verbose declaration and start using the new type-inferred syntax to save a few pixels.
Simplified varargs method invocation
This is one of the simplest changes of all – it just moves a warning about type information for quite a specific case where varargs combines with generics in a method signature. Put another way, unless you’re in the habit of writing code that takes as arguments a variable number of references of type T and does something to make a collection out of them, such as code that looks like this:
Then you can move on to the next section. Still here? Good. So what’s this issue all about?
Well, as you probably already know, a varargs method is one that takes a variable number of parameters (all of the same type) at the end of the argument list. What you may not know is how varargs is implemented. All of the variable parameters at the end are put into an array (which the compiler automatically creates for you) and are passed as a single parameter.
This is all well and good, but here we run into one of the admitted weaknesses of Java’s generics – you are not normally allowed to create an array of a known generic type. So, this:
HashMap<String, String> arryHm = new HashMap<>;
Won’t compile; you can’t make arrays of a specified generic type. Instead, you have to do this:
HashMap<String, String> warnHm = new HashMap;
Which gives a warning that has to be ignored. Notice that you can define warnHm to be of the type array of HashMap<String, String>. You just can’t create any instances of that type and, instead, have to hold your nose (or at least, suppress the warning) and force an instance of the raw type (which is array of HashMap) into warnHm.
These two features – varargs methods really working on the synthetic arrays that the compiler conjures up and arrays of known generic types not being valid instantiable types – come together to cause us a slight headache. Consider this bit of code:
The compiler will attempt to create an array to contain hm1 and hm2, but the type of the array should strictly be one of the forbidden array types. Faced with this dilemma, the compiler basically cheats and breaks its own rule about the forbidden array of generic type. It creates the array instance but grumbles about it, producing a compiler warning that mutters darkly about “uses unchecked or unsafe operations.”
From the point of view of the type system, this is fair enough. However, the poor developer just wanted to use what seemed like a perfectly sensible API and now there are these scary-sounding warnings for no adequately explained reason.
What’s changed in Java 7
Java 7 brought a change in the emphasis of the warning. After all, there is a potential for violating type safety in these types of constructions, and somebody had better be informed about them. There’s not much that the users of these types of APIs can really do, though. Either the code inside doSomething() is evil and violates type safety or it doesn’t. In any case, it’s out of the developer’s hands.
The person who should really be warned about this issue is the person who wrote doSomething() – the API producer, rather than the consumer. So that’s where the warning goes – it’s moved from the site of the API use (the warning used to be triggered when the code that used the API was compiled) to the site where the API was defined (so the warning is now triggered when an API is written, which has the possibility to trigger this kind of potential type safety violation). The compiler warns the coder implementing the API and it’s up to them to pay proper attention to the type system.
Changes to the type system
That’s an awful lot of words to describe a very small change. Moving a warning from one place to another is hardly a game-changing language feature, but it does serve to illustrate one very important point. Earlier in this paper, we mentioned that Project Coin encouraged contributors to mostly try and stay away from the type system when proposing changes.
This example shows how involved you need to get when figuring out how different features of the type system interact, and how that interaction will alter when a change to the language is implemented. This isn’t even a particularly complex change; larger changes would be far, far more involved with potentially dozens of subtle ramifications.
This final example illustrates how intricate the effect of small changes can be and completes our discussion of the changes brought in by Project Coin. Although they represent mostly small syntactic changes, once you’ve started using them in practice, you will probably find that they have a positive impact on your code that is out of proportion with the size of the change.
This article has been all about introducing some of the smaller changes in the syntax for Java 7. You saw that, although the changes are not earth-shattering, Java 7 will be a little bit easier to write in a more concise and error-free manner. You also learned that there can be challenges that cause language designers to make smaller and more conservative changes than they might otherwise wish.
We hope you enjoyed this article and look forward to discussing more about Java 7 and polyglot programming on the JVM with you in a pub near you soon!
- Strings in Switch
- Improved Exception Handling