Java 15 Features

Filed Under: Java
Java 15 Features

Keeping up with the six-month cycle tradition, after the release of Java 14 on March 17, 2020, we now have Java 15, the next non-LTS version rolling out on September 15, 2020.

Java 15 Features

Here’s a quick look at features that are a part of Java 15:

  • Sealed Classes (Preview) – JEP 360
  • Pattern Matching for instanceof (Second Preview) – JEP 375
  • Records (Second Preview) – JEP 359
  • Text Blocks (Standard) – JEP 378
  • Hidden Classes – JEP 371
  • Remove the Nashorn JavaScript Engine – JEP 372
  • Reimplement the Legacy DatagramSocket API – JEP 373
  • Disable and Deprecate Biased Locking – JEP 374
  • Shenandoah: A Low-Pause-Time Garbage Collector – JEP 379
  • Remove the Solaris and SPARC Ports – JEP 381
  • Foreign-Memory Access API (Second Incubator) – JEP 383
  • Deprecate RMI Activation for Removal – JEP 385

Java 15 Installation Setup on Mac OS

  • To get started with Java 15, download the JDK from here.
  • Copy and extract the tar file in the /Library/Java/JavaVirtualMachines as shown below:
$ cd /Library/Java/JavaVirtualMachines

$ sudo cp ~/Downloads/openjdk-15_osx-x64_bin.tar.gz /Library/Java/JavaVirtualMachines

$ sudo tar xzf openjdk-15_osx-x64_bin.tar.gz

$ sudo rm openjdk-15_osx-x64_bin.tar.gz

Once that’s done, open the bash_profile using any text editor. I’m using vim ~/.bash_profile. Set the path of Java15 to JAVA_HOME, save changes and do a source ~/.bash_profile to reflect the changes.

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-15.jdk/Contents/Home

Finally, you’re ready to compile and run programs using Java 15. We’ll be using JShell, an interactive REPL command-line tool for quickly testing the new Java 15 features.

It’s important to note that many features released in Java 15 are in preview. This means that though they’re fully working right now, things may be modified in the future. Some could be made a standard or simply removed in the next release cycles. In order to test preview features, you need to explicitly set --enable-preview when running the JShell or Java Program as shown below:

jshell --enable-preview

javac --release 15 --enable-preview Author.java

In the next few sections, let’s discuss the significant language changes in Java 15.

1. Sealed Classes (Preview)

Sealed classes have been there in Kotlin since a while and Java 15 finally introduces this feature for better control over inheritance.

As the name suggests, Sealed classes let you restrict or permit class hierarchies to only certain types.

This is incredibly useful for pattern matching as you have a specific number of classes to switch between.

The following syntax defines a sealed class in Java 15:

public sealed class Vehicle permits Car, Bike, Truck {
...
}

So, the above code means, only the classes defined after the keyword permits are allowed to extend the Vehicle sealed class.

In case, you’ve defined the classes Car, Bike and Truck in the same file as Vehicle, you can omit the keyword permits and the compiler will take care of it implicitly as shown below:

sealed class Vehicle {...}

final class Car extends Vehicle {...}
final class Bike extends Vehicle {...}
final class Truck extends Vehicle {...}

As you can see above, we’ve defined the final modifier of each of the classes. Now, that’s an important rule of sealed class that you need to keep in mind: Every permitted class must be set with an explicit modifier. It can either be final or sealed or non-sealed.

Here’s how each of the modifiers impact inheritance:

  • A permitted subclass that’s declared final cannot be extended further.
  • A permitted subclass that’s declared sealed can be extended further but only by classes that are permitted by the subclass.
  • A permitted subclass may be declared non-sealed can be extended further by any class. The superclass cannot restrict the subclasses further down this class hierarchy.

Before Java 15, developers could only use final keyword or scope modifiers to control inheritance. So, sealed classes brings the added flexibility for Java developers when defining class hierarchies.

Java’s Reflection API also gets two new methods for dealing with sealed classes:

java.lang.constant.ClassDesc[] getPermittedSubclasses();

boolean isSealed()

2. Records (Second Preview)

Records were introduced as a preview feature in Java 14, in an effort to reduce boilerplate code when writing  POJO based data carrier classes. This is something that’s been there in Kotlin for long in the form of data classes.

Now, with Java 15, Records get their second preview. While there aren’t any major changes(just some minor additions), still there are a few major clarifications and restrictions that you should know:

  • Prior to Java 15, one could declare native methods in records(though it wasn’t a good idea). Now the JEP explicitly prohibits against declaring native methods in records. Understandably, defining a native method steals away the USP of records by bringing an external state dependency.
  • The implicitly declared fields corresponding to the record components of a record class are final and should not be modified via reflection now as it will throw IllegalAccessException

Records are meant to be data carrier classes and you should totally avoid defining native methods in them.

Records with Sealed Types

We know that records are final and cannot be extended. Gladly, records can implement interfaces.

So you can define a sealed interface and implement them on your records in the following ways:

sealed interface Car permits BMW, Audi { ... }

record BMW(int price) implements Car { ... }

record Audi(int price, String model) implements Car { ... }

Local Records

Records can also be defined within methods to store intermediate values. Unlike local classes, a local record is implicitly static. This means they cannot access variables and instance members of the enclosing methods which is actually great since it prevents capturing of values by the record.

Local records are a great boon for Java developers who would earlier have to create helper records.

See how the introduction of a local records helps perform computation of values in a stream API with the following method:

List<Merchant> findTopMerchants(List<Merchant> merchants, int month) {
    // Local record
    record MerchantSales(Merchant merchant, double sales) {}

    return merchants.stream()
        .map(merchant -> new MerchantSales(merchant, computeSales(merchant, month)))
        .sorted((m1, m2) -> Double.compare(m2.sales(), m1.sales()))
        .map(MerchantSales::merchant)
        .collect(toList());
}

Conclusion

While the above two were the two major language features in Java 15, we also have Pattern Matching in the second preview for user feedback, Text Blocks as a standard feature now and most importantly a new Hidden classes feature.

Hidden classes are a JVM feature which is relevant for framework developers. It allows class implementations to be made non-discoverable by defining them using Lookup::defineHiddenClass. In doing so, such classes can neither be found using Class.forName nor by referring them in bytecode.

These are the major changes introduced in Java 15.

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages