What's new for developers in Java 15?

September 15, 2020
Written by
Reviewed by

Title: What's new for developers in Java 15?

[header image credit: NASA Astronomy Picture Of the Day, Sep 15 2020]

Happy Java 15 release day! This release marks three years since the start of the six-monthly release schedule and I am still appreciating the speed of new features and the insight it's giving into what's coming up in Java's future.

In Java 15 there are improvements to tooling, security, cross-platform consistency, efficiency and performance. You can read the full release notes and the list of major features, which are designated a Java Enhancement Proposal (JEP) number. In this post I've selected a few features which I think are interesting for developers.

The features I've selected are a mix of fully-released and "preview" features (more on these later).

How to use these features

You can download Java 15 from https://jdk.java.net/15/ - 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.

Release features

I've only chosen one release feature to go into detail in this post, but I think it's a great one:

Text blocks

Text Blocks (JEP 378) are multi-line strings for Java, with some differences to the standard String literal which make tex blocks especially suitable for embedding snippets of other languages in your code. I know I'm going to find this especially useful for embedding JSON in Java code, which is something I find myself doing a lot in tests.

A text block starts with three double-quotes and a newline, so the simplest one looks like this:

var textBlock = """
        This is a text block""";

If you print this string out, you will see that the newline and indenting whitespace are removed, so the first character is the T of "This". The same amount of indentation is removed from each line to pull at least one line of the text over to the left margin:

var textBlock = """
    this is a text block
  split over multiple lines""";

Line 2 starts with four spaces, and line 3 starts with two. So 2 spaces will be removed from the start of each line, and the string is:

  this is a text block
split over multiple lines

If you do need to include leading whitespace on every line, there is an instance method on String called .indent(int n) which can do that. This method was added in Java 12.

As well as stripping indentation, text blocks treat escape characters differently from regular strings. The general idea is that in most cases you don't need to use escapes in text blocks. So a snippet of JSON from NASA's Astronomy Picture Of the Day API could look like this:

var apodData = """
{
    "date": "2020-09-15",
    "title": "Biomarker Phosphine Discovered in the Atmosphere of Venus",
    "url": "https://apod.nasa.gov/apod/image/2009/VenusClouds_Akatzuki_960.jpg"             
}""";

You can see I haven't needed to escape the " marks, nor the newlines for that matter.

There are a couple of new escape characters which help you control how whitespace is handled. A single \ at the end of a line will run the two lines together (technically the escape sequence is \<newline>):

/// "this is on a single line"
var singleLineTextBlock = """
    this is on \
    a single line""";

The other new escape sequence is \s which is used to prevent whitespace-stripping at the end of a line:

var trailingWhitespace = """
    this ends with 8 spaces        \s""";
// normally removed -------^^^^^^^^---- but the \s means they are kept

The last thing to mention is that there is a new instance method on Strings called str.formatted(x,y,z) which does the same as the static String.format(str,x,y,z).

I'm sure developers will enjoy not having to wrangle embedded strings in Java from now on!

Preview features

Preview features are explicitly designed to get feedback from the Java community, so this is your chance to shape the future of Java! For an excellent discussion of what preview features do for the Java platform, including how to enable them with IDEs and build tools like Maven and Gradle, read Nicolai Parlog’s Evolving Java blog post. To use "preview" features, you need to add a command-line flag when you start the JVM: --enable-preview. If your IDE is up to date you can have it do the hard work for you:

Screenshot of configuring IntelliJ to use 15 (Preview) language features.

Here are my favourite preview features. Actually, it's all of the preview features, because I like each of them:

Pattern Matching for instanceof

I encourage every Java programmer to read the text of Java Enhancement Proposals (listed as "Features" on the release page). They are clear and thorough, and include sections on motivation, the goals and (importantly) non-goals for the feature, and plenty of examples. There's often interesting stuff in the "History" section, about different implementations or similar features which have been proposed and rejected. Fascinating stuff if you're into language design.

In the history of Pattern Matching for instanceof (Second Preview) (JEP 375) you can see "This JEP proposes to re-preview the feature in JDK 15, with no changes relative to the preview in JDK 14, in order to gather additional feedback." so you could check my Java 14 New Features for Developers post for full details.

Briefly, it allows you to perform a cast at the same time as an instanceof check, so

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

becomes

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

I think this specific case is a marginal improvement but pattern matching is a broad set of features addressed by many JEPs which can make code much smoother to read and write, and I'm excited to see where it goes.

Sealed classes and records

I've put these two JEPs together because the features can work nicely in unison.

Sealed classes

Sealed Classes (Preview) (JEP 360) provides fine-grained control over what subclasses can exist for your "sealed" class. Currently, if you're writing a class you have a fairly limited set of choices for controlling the subclasses. You can mark the class as final (no subclasses at all), or use access modifiers like private or protected. Sealed classes let you specify a full or partial set of subclasses in the superclass itself, by use of a couple of new keywords: sealed and permits. It looks like this:

public sealed class Shape 
  permits Circle, Rectangle, Square {
 /// shape implementation
}

public class Circle extends Shape {...}
 // Rectangle and Square defined in the same way

Now, if you are writing code which handles a Shape instance, you know for sure that it's a Circle, a Rectangle or a Square. Nothing else. The permitted subclasses can themselves be sealed which lets you create nested hierarchies of classes that are strictly limited.

The JEP looks ahead to supporting pattern matching on types, in this example:

int getCenter(Shape shape) {
    return switch (shape) {
        case Circle c    -> ... c.center() ...
        case Rectangle r -> ... r.length() ...
        case Square s    -> ... s.side() ...
    };
}

Without Shape being sealed there is no way to be sure at compile-time that this is an exhaustive list.

Further down the inheritance hierarchy, it's possible to open a particular class to unlimited subclassing by using another new keyword: non-sealed. For example, if Square was non-sealed we would still know that all Shape instances are Rectangle or Circle or Square, but there could be all kinds of Squares.

The sealed keyword interacts with other class modifiers, and interfaces can be sealed too. There are lots of examples in the JEP so I would encourage you to check that out.

Records

Records (JEP 384) are another feature in Second Preview. This time there are some differences from the first preview but they are minor - the gist of the Records is just the same, and they are still just as exciting.

Records are classes which by default contain:

  • a constructor
  • immutable properties (with getters)
  • implementations of hashCode/equals
  • a sensible toString implementation

Records are referred to in the JEP as "data-carriers". Creating classes which do the same as records is perfectly possible, but it's verbose and therefore hard to read and error-prone.  This is the most-used example of a Record:

record Point(int x, int y){}

The constructor, getters, equals and hashCode, toString are all generated from that definition, and you can add your own methods too:

record Point (int x, int y){
    public double distanceFromOrigin(){
        return Math.sqrt(x*x + y*y);
    }
}

I wrote in detail about records in my post on Java 14 and if you're thinking of using records to model JSON data, check Angie Jones's post on deserializing API responses into records.

Sealed records

Records are classes and they are implicitly final - you cannot extend a record. But you can permit a record to be a subclass of a sealed class or interface. Together sealed classes and records allow Java programmers to create types which are combinations of other types, sometimes known as Algebraic Data Types. Think of a sealed type as an OR (known as a "sum type") and a record as an AND (a "product type") and you can see how it becomes possible to reason logically about complex combinations of types.

This lets us model problem domains by treating types in powerful ways - we can hope that future work on pattern matching will take advantage of this, at least.

Summing up

Java 15 is another release with some great features and some pointers to the future of Java as a modern language which allows programmers to use whatever paradigm they prefer.

If you're working on a codebase which is still using an older version of Java, I hope this post has given you some ideas of what you are missing - and some arguments to make to your team about why you should move forward. And if you've already started using Java 15 then GREAT! I'd love to hear from you about how you are using the new features. Whatever you're building with Java, happy coding!

🐦 @MaximumGilliard

📧 mgilliard@twilio.com