What is new in Java 14 (for Developers)
Java JDK 14 was released on March 2020 as part of the new release cycle (a new Java version every 6 months). In this article we will learn what are the new features available for developers.
What’s new in Java 14?
Java JDK 14 is one of the releases I’ve personally been waiting for. It includes Switch Expressions (introduced in Java 12) - now as a complete feature, Text Blocks, Pattern Matching for instanceof (Preview) and my two favorite features: Helpful NullPointerExceptions and Records (Preview). Althought JDK 14 is not LTS (Long Term Support) it’s exciting to see the language evolving with features will have an impact in our daily professional lives as developers.
Below is a list of all features included in Java 14 (from Oracle blog):
- JEP 305 – Pattern Matching for instanceof (Preview): This preview feature enhances Java with pattern matching for the instanceof operator. This improves developer productivity by eliminating the need for common boiler plate code and allows more concise type-safe code.
- JEP 343 – Packaging Tool (Incubator): This incubator tool provides a way for developers to package Java applications for distribution in platform-specific formats. The tool helps developers with modern applications where constraints require runtimes and applications to be bundled in a single deliverable.
- JEP 345 – NUMA-Aware Memory Allocation for G1: This feature improves overall performance of the G1 garbage collector on Non-uniform memory access (NUMA) systems.
- JEP 349 – JFR Event Streaming: This feature exposes JDK Flight Recorder (JFR) data for continuous monitoring, which will simplify access to JFR data to various tools and applications.
- JEP 352 – Non-Volatile Mapped Byte Buffers: This feature adds a file mapping mode for the JDK when using non-volatile memory. The persistent nature of non-volatile memory changes various persistence and performance assumptions which can be leveraged with this feature.
- JEP 358 – Helpful NullPointerExceptions: This feature improves the usability of
NullPointerExceptions
by describing precisely which variable was null and other helpful information. This will improve developer productivity and improve the quality of many development and debugging tools. - JEP 359 – Records (Preview): This preview feature provides a compact syntax for declaring classes which hold shallowly immutable data. Superficially, this feature greatly reduces boilerplate code in classes of this type, but ultimately its goal is to better allow the modeling of data as data. It should be easy, clear, and concise to declare shallowly-immutable nominal data aggregates.
- JEP 361 – Switch Expressions: This was a preview feature in JDK 12 and JDK 13 and is now a completed feature. It allows switch to be used as either a statement or an expression. This feature simplifies every day coding and prepared the way for the pattern matching (JEP 305) feature previewed in this release.
- JEP 362 – Deprecate the Solaris and SPARC Ports: This JEP deprecates the Solaris and SPARC ports with the intent to remove them in a future release.
- JEP 363 – Remove the Concurrent Mark Sweep (CMS) Garbage Collector: The CMS garbage collector was deprecated over two years ago and G1, which has been the intended successor to CMS since JDK 6, has been the default GC and used at scale for many years. We have also seen the introduction of two new collectors, ZGC and Shenandoah, along with many improvements to G1 over the same time.
- JEP 364 – ZGC on macOS: While most users that require ZGC also require the scalability of Linux-based environments, it is also often needs, for deployment and testing, in Windows and macOS. There are also certain desktop applications which will benefit from ZGC capabilities. Therefore, the ZGC feature was ported to Windows and macOS.
- JEP 365 – ZGC on Windows.
- JEP 366 – Deprecate the ParallelScavenge + SerialOld GC Combination: This deprecates the combination of the Parallel Scavenge and Serial Old garbage collection algorithms, which is rarely used, with the intent to remove it in a future release.
- JEP 367 – Remove the Pack200 Tools and API: This removes the
pack200
andunpack200
tools, and the Pack200 API in thejava.util.jar
package. These tools and API were deprecated for removal in Java SE 11. - JEP 368 – Text Blocks (Second Preview): After receiving feedback when Text Blocks was first introduced as a preview feature (JEP 355) as part of Java 13, two new escape sequences have been added, and Text Blocks is being offered as a preview feature for a second time. Benefits of Text Blocks include: simplified writing of programs using strings that span several lines of source code, while avoiding escape sequences in common cases; enhanced readability of strings in Java programs that denote code written in non-Java languages; supports the migration from string literals by stipulating that any new construct can express the same set of strings as a string literal, interpret the same escape sequences, and be manipulated in the same ways as a string literal.
- JEP 370 – Foreign-Memory Access API (Incubator): This incubator module introduces an API to allow Java programs to safely and efficiently access foreign memory outside of the Java heap.
Let’s take a look at Switch Expressions, new features from Text Blocks, Helpful NullPointerExceptions, Pattern Matching for instanceof and the new Records.
Before you try these new features, don’t forget to download and install the latest JDK. Also, don’t forget to download the latest version of your favorite IDE (latest Eclipse version or IntelliJ IDEA 2020.1).
Pattern Matching for instanceof (from JEP 305 - Preview Feature)
Consider the following code, and nothe the instanceof
code inside the equals
method:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (obj instanceof Person) {
Person person = (Person) obj;
return age == person.age &&
name.equals(person.name);
}
return false;
}
}
Code such as Person person = (Person) obj
is very common in Java projects. Pattern Matching for instanceof
allows us to simplify this code. After refactoring with Java 14, this is what we have:
if (obj instanceof Person person) { // we can declare the variable person here
// Person person = (Person) obj;
return age == person.age &&
name.equals(person.name);
}
Although this is a very simple change, the code is more concise. And any refactoring we can do to improve our code, we’ll take it! :)
Text Blocks - String Literals (from JEP 368 - Second Preview)
A text block is a multi-line string literal that avoids the need for most escape sequences, automatically formats the string in a predictable way, and gives the developer control over format when desired.
The first preview of this feature was introduced in Java 13.
Below is a snippet comparing how we used to declare multiple line string and from first preview in Java 13:
private static void textBlocks() {
String beforeQuery = "update products\n" +
" set quantityInStock = ?\n" +
" ,modifiedDate = ?\n" +
" ,modifiedBy = ?\n" +
"where productCode = ?\n";
String updateQuery = """
update products
set quantityInStock = ?
,modifiedDate = ?
,modifiedBy = ?
where productCode = ?
""";
System.out.print(updateQuery);
}
The second preview of this feature introduced two escape sequence we can use.
First, the \
escape sequence explicitly suppresses the insertion of a newline character.
The \s
escape sequence can be used in both text blocks and traditional string literals.
Applying these two new escape sequence:
String updateQuery2 = """
update products \
set quantityInStock = ?
,modifiedDate = ?
,modifiedBy = ? \s \
where productCode = ?
""";
System.out.println(updateQuery2);
We’ll have the following output:
update products set quantityInStock = ?
,modifiedDate = ?
,modifiedBy = ? where productCode = ?
These two new escape sequence give us a little bit more flexiblity to write readable code without comprimising the way we want the output to be!
Switch Expressions (from JEP 361 - Complete Feature)
Switch Expressions are now much more optimized (syntax wise) than before. I have published details in these two blogs posts:
To summarize, we can use yield
to return a value from switch instead of break
:
private String switchJava13(DAY_OF_WEEK dayOfWeek) {
return switch (dayOfWeek) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
yield "Weekday";
case SATURDAY:
case SUNDAY:
yield "Weekend";
};
}
We can also use the case value ->
syntax (introduced in Java 12 as preview):
private String enhancedSwitchCase(DAY_OF_WEEK dayOfWeek) {
return switch (dayOfWeek) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday";
case SATURDAY, SUNDAY -> "Weekend";
};
}
Helpful NullPointerExceptions (from JEP 358)
This is one of my favortite JEPs this release!
Given the code below, we’re going to get a NullPointerException
because matrix[1][0]
is null
.
private static void helpfulNullPointerExceptions(){
String[][] matrix = new String[5][5];
matrix[1] = new String[5];
if (matrix[1][0].toUpperCase().equals("S")) {
// some code here
}
}
If we run code above with Java 13 or before, we’ll get the following exception:
Exception in thread "main" java.lang.NullPointerException
at com.loiane.Java14Features.helpfulNullPointerExceptions(Java14Features.java:33)
at com.loiane.Java14Features.main(Java14Features.java:20)
This stack trace is helpful, but not that helpful. We don’t know if the exception was thrown because matrix[1]
is null or matrix[1][0]
, and to find it out it requires some debugging.
This JEP introduces a JVM parameter that can show the exact reason of this exception. By default, this feature is set to false
, but we can enable it by adding -XX:+ShowCodeDetailsInExceptionMessages
to the JVM options as showed below:
According to the JEP details: “The option will first have default ‘false’ so that the message is not printed. It is intended to enable code details in exception messages by default in a later release.”.
Executing the code again, we’ll get the helpfull NullPointerException
:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because "matrix[1][0]" is null
at com.loiane.Java14Features.helpfulNullPointerExceptions(Java14Features.java:33)
at com.loiane.Java14Features.main(Java14Features.java:20)
Much better!
Records (from JEP 359 - Preview Feature)
This is another one of my favorites JEPs from this release!
From the JEP description: “Records are a new kind of type declaration in the Java language. Like an enum, a record is a restricted form of class. It declares its representation, and commits to an API that matches that representation. Records give up a freedom that classes usually enjoy: the ability to decouple API from representation. In return, records gain a significant degree of concision.”.
When we create a class to represent something in Java, we usually declare all the atributes/properties, then create the getters and setters, contructors, and methods toString
, equals
, hashCode
. Altough the IDE can create the methods for us, when reading the code later, it’s too many lines, and sometimes what we need is something very simple just to represent something.
There is an alternative to declutter the classes, which is done by project Lombok. Some developers like it, others don’t like it.
However, with the new Records, decluttering our code into a more elegant code is possible throuhg records!
Let’s take a look how we can declare a Record:
public record Product(String name,int quantity,double price) {}
That’s awesome! Now, this is what we get once the code is compiled:
public final class Product extends java.lang.Record {
private final java.lang.String name;
private final int quantity;
private final double price;
public Product(java.lang.String name, int quantity, double price) { /* compiled code */ }
public java.lang.String toString() { /* compiled code */ }
public final int hashCode() { /* compiled code */ }
public final boolean equals(java.lang.Object o) { /* compiled code */ }
public java.lang.String name() { /* compiled code */ }
public int quantity() { /* compiled code */ }
public double price() { /* compiled code */ }
}
All properties are private
and final
, which makes our properties immutable. We don’t get setters, and the getters don’t have the get
or is
prefix. We also get by default the methods toString
, hashCode
and equals
. Our Record is compiled into a class that extends the class Record
and it’s also a final
class, meaning we cannot declare an abstract Record
. And all the properties are declared within parenthesis ()
, meaning we cannot declare instance properties inside a Record
.
Because of this new type, some new methods were also added into the Java API. The Class
class has two methods for the new Record feature: isRecord()
and getRecordComponents()
(used to retrive details of annotations and the generic type),. The ElementType
enumeration has a new constant for Records: RECORD_TYPE
.
If we need to, we can override the default contructor to add any specific logic required:
public record Product(String name,int quantity,double price) {
public Product {
if (quantity < 1) {
throw new IllegalArgumentException("Product needs to have at least 1 quantity");
}
}
}
The constructor can also declare arguments/parameters:
public record Product(String name,int quantity,double price) {
public Product(String name, int quantity, double price) {
if (quantity < 1) {
throw new IllegalArgumentException("Product needs to have at least 1 quantity");
}
this.name = name;
this.quantity = quantity;
this.price = price;
}
}
But that’s now all we can do with a Record
. We can also declare static properties and methods:
public record Product(String name,int quantity,double price) {
private static int COUNT = 0;
static int count() {
return COUNT;
}
}
A Record
cannot extend another class (as the compiled code is a class that exteds Record
and Java does not allow multiple inheritance with classes), but a Record
can implement interfaces:
public record Product(String name,int quantity,double price)
implements Comparable<Product> {
@Override
public int compareTo(Product o) {
return 0; // code here
}
}
And finally, we can also add annotations to the Record’s properties:
public record Product(
@NotNull String name,
int quantity,
@Min(1) double price) {}
Personally, I can’t wait for this JEP to be a complete feature so I can start refactoring existing projects!
Conclusion
This was a big release for Java in number of JEPs, with some important features for developers. Now let’s wait for Java 15 (September 2020) with more new features!
View the full source code on GitHub.
References:
- The arrival of Java 14 - Oracle blog
- OpenJDK JDK 13 project page
- What’s New In JDK 14 Latest Release? 80 New Features & APIs
Happy Coding!