DevTalk
September 7, 2022
9
min read

Using Java 17 latest features in real world projects

Benedikt Bischof

Welcome to this issue of the WeAreDevelopers Live Talk series. This article recaps an interesting talk by Ron Veen who introduced the audience to some of the most interesting features that got added to the Java language.

About the speaker:
Ron is a highly experienced software engineer who has seen it all from Midrange Systems to Micro Services.
Through all of this, he was guided by his passion for software engineering and software architecture.
For more than 15 years he has been working on the JVM and the Java ecosystem and has seen all the frameworks and libraries from Apache to ZK.
He is an avid fan of alternative JVM languages like Groovy, Scala, Clojure, and his personal favourite Kotlin.
Ron is an Oracle Certified Java Programmer (OCP) and Sun Certified Business Component Developer (SCBCD/OCPBCD) and a regular speaker at conferences.
Recently he talked at the WeAreDevelopers World Congress 2022.

Introduction

Ron starts his talk by greeting the audience and stating how awesome of a time it is to be a Java enthusiast, because the newest version (Java 19) is currently in development. As you might have heard, this is the first time we will see some features of Project Loom sneaking into the language of Java. So, we will get virtual threads and structured concurrency, which is a big step forward according to Ron and will change the way we look at concurrency and concurrent programming in Java. There's a lot to look forward to, but that’s not why he made this talk. Instead, it’s all about language features that were added in the past, or more precisely, since Java 12. Although there is a great amount, Ron wants to focus on five very interesting and useful ones. 

Switch expression

The first added feature he discusses was introduced as a preview in Java 12 and became a final version in Java 14.
The switch statement has been part of the Java language since day one and is very similar to that of the basic C language. But since its implementation we have the Switch expression:

private void run(int value) {

     String answer = switch (abs(value)) {

         case 0 -> "Zero";

         case 1,2,3,4,5  -> "Five or less";

         case 42 -> "The answer to life, the universe and everything";

         default -> "Not sure, but more than six";

     };

System.out.println(answer);

}

As you can see it's kind of a function which returns a value. You call it with a specific value with the typical “case” instruction. But instead of the usual return statement we now only need an arrow with a value after it. Take note that there is only one value allowed per arrow unless you use a block. We call that a switch-labeled rule. And because it returns something and is therefore kind of a function you need a default statement, as written in the code example.

But rather than just one we actually got two versions of the switch expression in Java. The other one is called a switch-labeled statement group and does exactly what the name suggests. Take a look at the code snippet for this one:

private void runAnother(int value) {

     String answer = switch (abs(value)) {

         case 0:

             System.out.println("Value is zero");

             yield "Zero";

         case 1,2,3,4,5:

             System.out.println("Value is between 1 and 5");

            yield "Five or less";

         case 42:

             System.out.println("42!!!");

             yield "The answer to life, the universe and everything";

         default:

             System.out.println("Another value");

             yield "Not sure, but mor than six";

     };

     System.out.println(answer);

}

Here you can make multiple statements and need to end every case with a “yield”. This indicates that you made several statements and the part after the yield is the value that should be returned.

Text Blocks

Coming up with the next handy feature, Ron introduces us to the concept of Text Blocks. Of course, you all know how it is to have a chunk of code in which multiple strings are needed to be concatenated together so you have a lot of pluses in between them. And it gets even worse when you have quotations in it, so you always need to escape them. Code like that is not only annoying to write, but it’s also hard to read and very error-prone.

But don’t worry, we now have Text Blocks:

private void run() {

     System.out.println("""

<html>

 <body>

<p>

    <div>Finally some text with "</div>

""");

}

With this feature, you need to start with triple quotes and afterwards just write the text as you want it to be. Another handy advantage is that you can use quotes within the text itself, so no more escaping is needed here. All that should make your coding life much easier.

Records

Third, we have the probably most discussed and later most loved feature that was added to the Java language: Records. That one got the first preview features in Java 14 and became final in Java 16.

So, this is what a Record basically looks like:

public record Conference(String eventname, String location, LocalDate date, Boolean inPerson) { }

You declare a public record and then you specify all the names of the instance variables and what type they are. Those are actually final, which means once the constructor is completed, they will have their final value and are from then on immutable.

After you run the compiler, it will generate an equal method if you want to do some sort of comparison with them. You will also get the hash code to make it suitable for collections and of course, the compiler will generate also what we used to call a getter–method, but it’s a bit different with Records as there are no more getters (and certainly no setters for that matter). Ron calls them the accessor methods:

 var eventLocation :String = conference.location();

So, here we have the conference record with a location variable. As you can see you don’t need some sort of get.location, you can just use the name.

It’s the same when it comes to creating:

var conference = new Conference( eventname: “WeAreDevelopers”, location: “Berlin”, date: null, inPerson: true);

As all the instance variables are final ones you have to provide all the values in an all-arguments-constructor.

Sealed Classes

When looking at object inheritance in Java, you basically got two options. Either mark the class as final so no one can inherit from it, or you don’t mark it and technically everyone can inherit from it. There are of course some access modifiers as well but in general, these are the two flavours to choose from. So how can you control which classes or interfaces are allowed to subclass your classes and interfaces? Here the Sealed Classes come into play:

package nl.rockstars.sealed;

public sealed interface Plugin permits PrinterPlugin, ExportPlugin, UserDefinedPlugin {

         void execute();

}

Basically, you must define your class as “sealed”, as shown in the code snippet. After that, you have to tell who is allowed to subclass it. In this case for example “PrinterPlugin”, “ExportPlugin” and “UserDefinedPlugin” are allowed to do so. Any other class that tries will get a compile-time warning. But it doesn’t end here, as each of these three classes have to specify for themselves how to handle inheritance:

public sealed class ExportPlugin implements Plugin permits JPEGExport, PDFExport {

         @Override

         public void execute() { }

}

So the ExportPlugin states that there are two different ways to export, either as a jpeg or as a pdf. So you call it a sealed class again, further limiting the inheritance options.

public final class PrinterPlugin implements Plugin {

         public void execute()  { }

}

Continuing with the PrinterPlugin that’s a bit easier as there is only one and therefore is defined as final meaning no one can subclass it.

public non-sealed class UserDefinedPlugin implements Plugin {

         public void execute() { }

}

And finally, the UserDefinedPlugin uses the third keyword “non-sealed”. That basically states that anyone can subclass it.

Pattern Matching

The fifth and final feature Ron is talking about is Pattern Matching which is actually a two-stage process. First, you apply a predicate or test to an object. If that test is successful then certain values of that object will be extracted into pattern variables.

Let’s have a look at the code example from the last section and modify it a bit:

package nl.rockstars.sealed;

public sealed interface Plugin permits PrinterPlugin, ExportPlugin, UserDefinedPlugin {

         void execute();

}

public non-sealed class UserDefinedPlugin implemements Plugin {

         public void execute() { }

         public void initExtended() {}

}

 

public void extendedInit(Plugin plugin) {

         if (plugin instanceof UserDefinedPlugin udp) {

                     udp.initExtended();

         }

}

So, we had this interface that allowed some classes to sublass it. In the non-sealed class, we can define an extra method (initExtended) and if that we do a test. And if that is successful then udp will be an instance of UserDefinedPlugin.

Thank you for reading this article. If you are interested in hearing Ron Veen himself you can do so by joining our event platform.

Using Java 17 latest features in real world projects

September 7, 2022
9
min read

Subscribe to DevDigest

Get a weekly, curated and easy to digest email with everything that matters in the developer world.

Learn more

From developers. For developers.