In this post, we’ll look at some of the latest features introduced in Java 10. Let’s go.
Here’s a list of features covered in this post so you could skip to the one you don’t know about or skip this post entirely if you know them all :-)
- The try-with-resources Statement
- Catch Multiple Exceptions in a Single
catch
Block - Underscores in Numeric Literals
- Default Methods (in Interface)
- Parallel Sorting of Large Arrays
- Optional Return Values
- Strings in
switch
statements - The Diamond Operator
<>
- Annotations Everywhere
- Varargs
A word of caution: There’s no harm in doing things the old fashioned way if it is working. It is much better than rushing to use a feature that is not fully understood. But knowledge is power.
Without further ado, let’s get started.
1. The try-with-resources Statement
Prior to Java 7, working with InputStream
(and other similar APIs) produced ugly looking code. Here’s an egregious example.
InputStream is = new FileInputStream("unreadme.txt");
try {
// Read the file
} catch(IOException e) {
// Handle if an exception occurs while reading the file
} finally {
try {
if(stream != null) {
stream.close();
}
} catch(IOException e) {
// Handle if an exception occurs while closing the file
}
}
There’s a lot of noise in the code above and twin try-catch
statements. It is unnecessarily verbose, even by Java standards.
The try-with-resources statement addresses this issue. You declare any object that implements java.lang.Closeable
at the start of the block. When the block exits, Java automatically closes the object by calling its close()
method. Let’s rewrite the above example using try-with-resources:
try(FileInputStream is = new FileInputStream("unreadme.txt")) {
// do something with the file
} catch(IOException e) {
//...
}
Much cleaner. You can even specify multiple Closeable
objects.
2. Catch Multiple Exceptions in a Single catch
Block
Since Java 7, multiple exceptions can be caught in a single catch
block which makes the code less verbose and avoids duplication. Here’s an example of the multi-catch block:
try {
// Code that throws multiple exceptions
} catch (IndexOutOfBoundsException | IOException ex) {
logger.error("err", ex);
}
Instead of the usual:
try {
// Code that throws multiple exceptions
} catch (IndexOutOfBoundsException oobe) {
logger.error("trouble traversing", oobe);
} catch (IOException ioe) {
logger.error("problem reading file", ioe);
}
Also, don’t do this:
try {
// Code that throws multiple exceptions
} catch (Exception e) {
logger.error("an error occurred", e);
}
Catching Exception
is generally a bad idea. The catch
block in the example above will catch all exceptions that are thrown in the try
body including the ones that it cannot handle. This prevents upper methods in the stack from handling the exception properly.
The only catch is that the exceptions in multi-catch must be disjoint. See this Stack Overflow answer for more details.
3. Underscores in Numeric Literals
Starting from Java 7, you can use underscores in numeric literals to make your code more readable. The underscores can appear anywhere between the digits, except at the start or at the end of literal. Here are some examples taken from Oracle’s tutorial:
long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
long phoneNumber = 123_456_7890L;
long data = 0b11010010_01101001_10010100_10010010;
byte nybbles = 0b0010_0101;
long maxLong = 0x7fff_ffff_ffff_ffffL;
int x6 = 0x5_2;
Although, I’m not sure if it would ever make sense to store credit cards or social security numbers in your code, using underscores certainly make reading hex and binary variables a lot more convenient.
4. Default Methods (in Interface)
Since Java 8, you can include method bodies to interfaces which wasn’t allowed in the previous versions of Java. These methods are known as the default methods because they are automatically included in classes that implement the interface. Default methods were added primarily for backwards compatibility reasons and you should use them judiciously.
This blog has a good overview of the subject.
5. Parallel Sorting of Large Arrays
This one’s my favorite. It allows larger arrays to be sorted faster. It works by dividing a large array into several smaller subarrays and then sorting each subarray concurrently or in parallel. The results are merged back together to form the answer. So instead of,
Array.sort(someArray); // Sorts arrays sequentially
Starting with Java 8, you could use:
Arrays.parallelSort(someArray); // Parallel Sorting
The analysis done for this article of Dr. Dobb’s got 4x better performance:
I loaded the raw integer data from an image into an array, which ended up at 46,083,360 bytes in size (this will vary depending on the image you use). The serial sort method took almost 3,000 milliseconds to sort the array on my quad-core laptop, while the parallel sort methods took a maximum of about 700 milliseconds. It’s not often that a new language release updates the performance of a class by a factor of 4x.
4x!? I’d be very pleased with anything over 2x.
6. Optional Return Values
Java 8 has introduced a container object called Optional
for wrapping objects that may not be present or null
. A method can wrap its return type in Optional
. Let’s look at an example:
public static Optional<User> getUser(String id) {
// If the user id is NOT FOUND, return null
return null;
}
And here’s how to call the method:
Optional<User> optional = getUser("jhal1");
if (optional.isPresent()) {
// User found. Get the value
User user = optional.get();
} else {
// No user found
}
In case you are wondering, what’s wrong with just returning the null
value? Callers aren’t always aware that method may return null
and do not always check for it. This happens frequently and is one of the reasons why the NullPointerException
s are so plentiful. So, if you are tired of null pointer exceptions, use Optional
.
7. Strings in switch
statements
I almost forgot that Java has a switch-case
statement. Prior to Java 7, switch-case
statement only worked with integer types (except long
) and enums
. Java 7 introduced the ability to use a String
object as the expression. Here’s how it looks:
String car = getCar();
switch(car) {
case "Corvette":
//Handle Corvette
break;
case "AC Cobra":
//Handle AC Cobra
break;
case "McLaren F1":
//Handle McLaren F1
break;
default:
//Handle "Car not Found" error
break;
}
Which is equivalent to the more cluttered:
if (car.equals("Corvette")) {
//Handle Corvette
} else if (car.equals("AC Cobra")) {
//Handle AC Cobra
} else if (car.equals("McLaren F1")) {
//Handle McLaren F1
} else {
//Handle "Car not Found" error
}
8. The Diamond Operator <>
Diamond operators were introduced to make the use of generics a little less verbose. Take a look at this example:
Map<String, List<String>> aMap = new HashMap<String, List<String>>();
The parameter types are duplicated on the left and right sides of the expression. Since Java 7, you can omit the type definitions on the right side of assignment expressions with a diamond operator, <>
. The above statement could be rewritten as:
Map<String, List<String>> aMap = new HashMap<>();
When the compiler encounters the diamond operator (<>), it infers the generic type arguments from the context.
9. Annotations Everywhere
Thanks to Java 8, annotations can be retrofitted almost anywhere in your code. Great, because that’s just what we needed. I’m fine with annotations, but I’ve seen some developers going overboard, trying to do too much magic with annotations. Too much of annotations, just like too much of anything, are bad. You’re probably better off not knowing that this feature even exists. End of my rant.
10. Varargs
Varargs are useful for passing an arbitrary number of parameters to a method. Such as String.format(String format, Object...args)
. Joshua Bloch in Effective Java recommends using varargs judiciously:
varargs are effective in circumstances where you really do want a method with a variable number of arguments. Varargs were designed for printf, which was added to the platform in release 1.5, and for the core reflection facility (Item 53), which was retrofitted to take advantage of varargs in that release. Both printf and reflection benefit enormously from varargs. You can retrofit an existing method that takes an array as its final parameter to take a varargs parameter instead with no effect on existing clients. But just because you can doesn’t mean that you should!
That’s sound advice. Just because there’s a feature available, doesn’t mean you have to use it.
Comments (3)
pronto
Warning about the use of Optional type (#6). It’s pointless and doesn’t solve anything. It was added for the Streams API and has no use outside of that domain.
Umer Mansoor
As a return type for methods that may not produce a value, the use of Optional allows developers to build more expressive, more comprehensible APIs and explicitly forces the caller to deal with the absence of a value. So I wouldn’t call it pointless or entirely useless. Except as the return type, I can’t think of any other place where the use of Optional would make sense, but there’s certainly no harm in using Optional if it might prevent a few unintended
NullPointerException
s.negative reviews
At least you were able to share it and give some sound of it. This will surely help a lot of people in getting what’s the best on their work and help them to achieve the most effective type of output in programming that they can have.