Java 14 New Features for Developers Graded

March 16, 2020
Written by

Java 14 New Features for Developers Graded

Java will celebrate its 25th birthday this May, and just before that, on March 17th Java 14 will be released. As well as language features, every new release has included lots of improvements in tooling, security, debugging, efficiency and of course performance. All the while the Java team has maintained backward compatibility so that you can always use code written for previous versions.

"Developer productivity and program performance, in the face of evolving paradigms, application areas, deployment styles and hardware."

    - Mark Reinhold on Java’s long-term design goals

Since Java 9, releases have come regularly every 6 months. However many folks will be reluctant to update Java in all their environments twice a year. For developers it’s well worth investigating each release to see what new features are being added, even if you won’t use them in production right away.

With Java 14’s release approaching fast, this post gives a quick look at some of the new features being released for developers, and some which will be added as “preview” features for testing and feedback.

How to use these features

Until March 17th, Java 14 will be available as an Early Access release. You can download it from https://jdk.java.net/14 - the same page will host the General Availability downloads on release, and downstream builds will be available from places like AdoptOpenJDK soon after. You can also install it using SDKMAN! which is my preferred way of dealing with multiple Java versions.

IntelliJ IDEA is my favourite IDE, which has an Early Access Programme whose latest release already supports JDK14 including preview features.

Eclipse also supports Java 14 in the 2020-03 release.

What’s Available in Java 14?

I’ve given each new feature a grade - this is my personal opinion based on my own unique experiences and opinions. You'll probably disagree with some of my grades, and that's good. Your opinions and experiences are different from mine. Let me know in the comments or on Twitter where you feel I went wrong.

Helpful NullPointerExceptions

An error message improvement that was surprisingly contentious. This improves life for developers who encounter NullPointerExceptions by describing exactly what was null.  Imagine a is null in:

a.i = 99;

The error message used to be:

Exception in thread "main" java.lang.NullPointerException
    at Prog.main(Prog.java:5)

And can now be:

Exception in thread "main" java.lang.NullPointerException: 
        Cannot assign field "i" because "a" is null
    at Prog.main(Prog.java:5)

I say “can”, because this change needs a new command-line argument to switch it on. Because of the Java team’s commitment to backwards compatibility, changing something even as innocuous as an error message is considered a “breaking change”, so to see the new messages use:

java -XX:+ShowCodeDetailsInExceptionMessages

For me, this is really useful - chasing NPEs is tiresome, especially when your code has several operations on the same line. There was some resistance to this change because of concerns over performance (the final implementation has smoothed that out), as well as the chance of leaking sensitive source-code details, for example into log files. The description of the feature says “it is intended to enable … by default in a later release”.

Grade: A this is going to be really helpful for beginners to Java - I hope it’s enabled by default soon.

 

Switch Expressions

The switch keyword has been part of Java for a long time. It lets you test a variable and execute different code paths depending on the value:

enum Bear { MUMMY, DADDY, BABY }

public static String getBedSoftness( Bear thisBear ){
   String result = "The bed is ";

   switch (thisBear) {
      case DADDY:
          result += "too hard";
          break;
      case MUMMY:
          result += "too soft";
          break;
      case BABY:
          result += "jusssst right!";
          break;
   }

   return result;
}

This syntax has a few problems. Firstly, the break statements are necessary, otherwise cases will “fall through”, which could lead to nonsense like “The bed is toohardtoo softjusssst right!”. But they’re noisy and fall-through is almost never what I want.

Java 14 adds a new syntax of case XXXX -> code which is just the same but doesn’t have fall-through:

switch (thisBear) {
   case DADDY -> result += "too hard";
   case MUMMY -> result += "too soft";
   case BABY -> result += "jusssst right!";
}

This is already nicer, but there’s a second change in 14 as well: switch can be used as an “expression” as well as a “statement”. What’s the difference?

    Expressions have a "value", statements don’t.

Using switch as an expression, the above method can be written as:

public static String getBedSoftness(Bear thisBear) {
   return "The bed is " +
          switch (thisBear) {
                       case DADDY -> "too hard";
                       case MUMMY -> "too soft";
                       case BABY -> "jusssst right!";
                   };
}

We can see that the switch is now being used as a value which is added directly onto “The bed is”.

I think this feature is a decent improvement for code readability by itself. It’s also part of the work preparing for “pattern matching” - a popular feature in other languages where the case statement can take a function that returns true or false, rather than just a list of values.

Grade: B Useful by itself in some situations - expressions often lead to cleaner code but full pattern matching is what I’m really excited about.

 

Preview Features

With the change to the faster release cycle, Java language features can be released as “previews”. When it is in preview, a language feature is not enabled by default but can be switched on by a command-line flag: --enable-preview. If you’re using the EAP version of IntelliJ this can be as simple as setting the “Project Language Level” on the “Project Structure” dialog to “14 (Preview)”.

IntelliJ's project structure window, highlighting the choice of "14 (Preview)" as Project SDK

For an excellent discussion of what preview features do for the Java platform, including how to enable them with other IDEs and build tools like Maven and Gradle, read Nicolai Parlog’s Evolving Java blog post. Preview features are explicitly designed to get feedback from the Java community, so this is your chance to shape the future of Java!

Here are some preview features you can try out with Java 14:

Text Blocks

Text blocks (aka multi-line strings) are helpful in a lot of cases, especially when you want to embed another language in your Java source. This is a good example of the preview system working to evolve the language with user feedback as it was previewed in a different form in Java 13 too.

With text blocks embedded JSON can look like this:

String testJson = """
   {
       "fruit": "Apple",
       "size": "Large",
       "color": "Red"
   }""";

Note that leading spaces on each line of the string are stripped so that the text is as far left as possible - the same number of spaces will be removed from each line, so that relative indentation is preserved. Also trailing spaces will be removed. In both cases, you can prevent the stripping by using a new escape character \s which represents a space which will not be removed.

Grade: A anyone who’s embedding JSON or SQL in Java code is going to love this.

Pattern Matching for instanceof tests

Another step toward pattern matching is being able to improve code like this:

if (o instanceof String) {
   String s = (String)o;
   System.out.println(s);
}

Into this:

if (o instanceof String s) {
   System.out.println(s);
}

Immediately you can see that it cuts a lot of noise from the code - the repeated mention of String is gone, as is the explicit local variable definition. It’s also possible to put extra guards on the condition like this:

if (o instanceof String s && s.length() > 2) {
   System.out.println(s);
}

For me instanceof checking always feels a little dirty. But on at the Java Enhancement Proposal page, there’s this hint at something more:

Future JEPs will enhance the Java programming language with pattern matching for other language constructs, such as switch expressions and statements.

This sounds like it would combine with switch expressions to allow code that might look like this:

switch (n){
   case n%15 == 0 -> "FizzBuzz";
   case n%5  == 0 -> "Buzz";
   case n%3  == 0 -> "Fizz";
   default -> Integer.toString(n);
})

If you’ve used constructs like this in other languages I’m sure you’ll be glad to see it coming to Java - pattern matching can allow really powerful and compact code.

Grade: C I’m not very excited by this feature by itself but looking forward to what comes from it.

Records

Imagine that you want to create some way to represent a marker on a map. You might think of creating a class, containing at a minimum: double latitude, double longitude and String description. So how about a simple class like this:

class Marker {
   double latitude, longitude;
   String description;
}

Of course, you care about being able to safely use these classes in multi-threaded code. In which case, don’t forget to make the members public final - no need for getters and setters in this case, but we now need a constructor. Oh, and it might be nice to be able to tell if two Marker objects refer to the same marker, or use a Marker as a key in a HashSet, so we need to implement hashcode and equals. And for debugging, it’s nice to have a good toString method.  Now the code looks like this:

class Marker {
   public final double latitude, longitude;
   public final String description;

   public Marker(double latitude, double longitude, String description) {
       this.latitude = latitude;
       this.longitude = longitude;
       this.description = description;
   }

   @Override
   public boolean equals(Object o) {
       if (this == o) return true;
       if (o == null || getClass() != o.getClass()) return false;
       Marker marker = (Marker) o;
       return Double.compare(marker.latitude, latitude) == 0 &&
           Double.compare(marker.longitude, longitude) == 0 &&
           Objects.equals(description, marker.description);
   }

   @Override
   public int hashCode() {
       return Objects.hash(latitude, longitude, description);
   }

   @Override
   public String toString() {
       return "Marker{" +
           "latitude=" + latitude +
           ", longitude=" + longitude +
           ", description='" + description + '\'' +
           '}';
   }
}

Hands up if you have code like that in your current project 🙋

I admit it, I do too. I got my IDE to write most of it so it didn’t take long and I’m pretty sure it’s correct. But code is read a lot more often than it is written, and it’d be easy for a bug to sneak in while updating this class (say, adding a new field).

In JDK14 a new language feature is being previewed, specifically to address this kind of boilerplate: Records. With --enable-preview we can write the above as:

record Marker(double latitude, double longitude, String description){}

That’s it. The constructor, hashcode, equals and toString will all be generated by the compiler, and the fields will be final. Records are themselves final, and can also not extend classes although they can implement interfaces. You can add methods to the body of the record if you need to, and you can skip field initialization if you need to do any special logic in the constructor:

public Marker {
   if (description.isBlank()) throw new IllegalArgumentException("We need descriptions");
}

Once they’re defined, records can be used just as you would use any class.

I’m delighted to see this feature - so many of the classes I write are just “data holders” and this makes it a lot easier to create and maintain them.

Grade: A+ I’ll be using records all the time, and I bet you will too.

 

Summary

These are the features of Java 14 which I think will make the biggest difference to developers. There are several other changes coming too which will affect performance and monitoring, as well as the removal of some older JVM features which have been superseded. You can see the full list on the project page, and don’t be afraid to dive into the details from there - they’re quite understandable.

If you try any of these features out, I’d love to hear about it. Get in touch: on Twitter I go by @MaximumGilliard, or you can email me on mgilliard@twilio.com.