Keeping Pace With What’s New in Java 14

These are comments to this video.

alex_ber
8 min readAug 28, 2020

Note: Original video also talks about Sealed classes and interfaces (preview language feature) in Java 15 to be released in September 2020. You can read about this here https://www.infoq.com/articles/java-sealed-classes/

Records (preview language feature)

Based on https://dzone.com/articles/a-first-look-at-records-in-java-14

UPDATE

After publishing original post, I’ve read original JEP for records and I was astonished to find the following:

While it is superficially tempting to treat records as primarily being about boilerplate reduction, we instead choose a more semantic goal: modeling data as data…Records go well with sealed types (JEP 360); records and sealed types taken together form a construct often referred to as algebraic data types. Further, records lend themselves naturally to pattern matching. Because records couple their API to their state description, we will eventually be able to derive deconstruction patterns for records as well, and use sealed type information to determine exhaustiveness in switch expressions with type patterns or deconstruction patterns.

https://openjdk.java.net/jeps/359

So, Java’s records are essentially product types. It is not just about reducing boilerplate. Practically, records are needed for pattern matching that is designed to be added in future version of Java. I will write separate post about this later.

In the video there is answered question. Why records doesn’t follows JavaBean naming convention?

…Like an enum, a record is a restricted form of class. It declares its representation, and commits to an API that matches that representation… A central aspect of Java’s philosophy is that names matter… That is, a Person class with properties firstName and lastName is clearer and safer than an anonymous tuple of String and String.

https://openjdk.java.net/jeps/359

END OF UPDATE

This is similar to data class in Kotlin. This is special kind of class that is intended to hold pure data in it. Typical use-cases are: DTO — data transfer object or domain model class.

Code example:

Note, there is mistake. Records doesn’t implements java.io.Serialzable by default. I think, this is bug in IntelliJ IDEA.

Actually, it is demonstrated later in the video.

You can see in line 5 in the link provided above of the decompiled class there is no mentioning of java.io.Serializable.

final class examples.Person extends java.lang.Record

Record class is final. It extends java.lang.Record, which is the base class for all records, much like java.lang.Enum is the base class for all enums. There is a public constructor that is generated for us. For each field there is getter-method to it with exact same name (no “get” prefix!). Three other methods are generated: toString(), hashCode() and equals(). They all rely on invokedynamic to dynamically invoke the appropriate method containing the implicit implementation. See here https://alex-ber.medium.com/explaining-invokedynamic-dynamical-hashcode-implementation-part-v-16eb318fcd47 for more details.

Another code example:

What are old ways to define record?

  1. Use of some unrated existing class. One popular one is SimpleEntry (because Pair is not present in the JDK). The main disadvantages:
  • we’re losing meaningful names , moreover, the names are misleading — getKey() and getValue() and not,say, getFirstName(), getLastName().
  • We are using the same type for different DTO/Model class, so compiler can’t help us to distinguish the use cases and we don’t have semantic information in what this partial DTO/Model class holds.

I’ve seen such usage couple of times also.

2. Class with public fields only.
Actually, from time to time I’m encountering with such solution in different projects. The declaration is short, but it goes with following drawbacks:

  • You can’t add field validation. If you have double amount, you can’t validate precondition on state that amount≥0for example.
  • If you have some complex state you don’t have place to handle it. Typically, it is done in setter-method. The work-around is to add such complex setter-method. The drawback is nobody expect that you class has such method, because you state is defined as set of public fields that are assigned directly.
  • You can’t make such object immutable, you have access to it to read the value, so you can also write to it.
  • You don’t have readable toString(), etc for debugging. If you start to add them, it takes just a little more effort to convert this field to private and to add getter-method to it. See next bullet.

3. Full-blown class, may be as JavaBean. Write down whole boilerplate code manually.

3. Full-blown class, using modern IDE to generate most the boilerplate code. This my currently personally preferable way. The main drawbacks are:

  • poor readability — you need to read a lot of irrelevant code; I’m mitigating this using naming convention, DTO suffix for the class name, etc.
  • difficulty of code modification — If you need to add new field, you should regenerated all equals()/hashCode()/toString()methods. If you have some manual change (for example, you’re omitting some field for equality test), you should remember to redo it after automatic code generation. I’m mitigating this by looking on code diff after I’m regenerated the code and careful investigation of such code. In practice, I’ve never deleted such code.

4. Use Project Lombok. Quote: “Project Lombok is a java library that…plugs into your editor and build tools, spicing up your java. Never write another getter or equals method again, with one annotation your class has a fully featured builder.” Lombok uses annotation processing through APT (Annotation Processing Tool), so, when the compiler calls it, the library generates new source files based on annotations in the originals. The main drawback for me is incompatibility with Spring Boot:

The properties that map to @ConfigurationProperties classes available in Spring Boot, which are configured via properties files, YAML files, environment variables etc., are public API but the accessors (getters/setters) of the class itself are not meant to be used directly…

Some people use Project Lombok to add getters and setters automatically. Make sure that Lombok does not generate any particular constructor for such a type, as it is used automatically by the container to instantiate the object.

https://docs.spring.io/autorepo/docs/spring-boot/2.4.0-M2/reference/htmlsingle/#boot-features-external-config-java-bean-binding

It is very difficult to debug Lombok. It took me couple of hours to make @ConfigurationProperties works (it was completely my fault, the problem was in wrong configurations in property files), at the time I just don’t want to spend extra time to configure Lombok (that is not trivial, but shouldn’t be hard) and then if it didn’t work I can’t debug it (I know how to be debug Spring Boot).

Nevertheless, I know a lot of people that happily use Lombok (even with Spring Boot, just they don’t use @ConfigurationProperties at all).

Pattern match for instanceof (preview language feature)

Based on https://www.baeldung.com/java-pattern-matching-instanceof

Old way:

New way:

Quote:

Let’s understand what is happening here. In the first, if block, we match animal against the type pattern Cat cat. First, we test the animal variable to see if it’s an instance of Cat. If so, it’ll be cast to our Cat type, and finally, we assign the result to cat.

It is important to note that the variable name cat is not an existing variable, but instead a declaration of a pattern variable.

We should also mention that the variables cat and dog are only in scope and assigned when the respective pattern match expressions return true. Consequently, if we try to use either variable in another location, the code will generate compiler errors.

https://www.baeldung.com/java-pattern-matching-instanceof

TextBlocks (second preview)

See https://www.baeldung.com/java-text-blocks
https://www.baeldung.com/java-multiline-string

Text blocks just provide us with another way to write String literals in our source code. The result type of a text block is still a String.

Inside the text blocks, we can freely use newlines and quotes without the need for escaping. It allows us to include literal fragments of HTML, JSON, SQL, or whatever we need, in a more elegant and readable way.

In the resulting String, the (base) indentation and the first newline are not included.

Code example:

This is equivalent to:

Another popular alternatives are:

  • To copy multi-line string and paste in inside two double quotes in modern IDEs. Many modern IDEs support multi-line copy/paste. Eclipse and IntelliJ IDEA are examples of such IDEs.
  • To store multi-line string in a text file. Use some utility method to read it from file (prior Java 8 I’ve used FileUtils and IOUtils from commons-io package), for example:

The reason for second preview is addition of two escape sequences.

  • Escaping Line Terminators — we can ignore new line.

This is equivalent to:

Note: I’m frequently using this escape sequence in Python.

  • Escaping Spaces — we can preserve any spaces spaces in front of new escape sequence \s.

This is equivalent to:

Note: the spaces in the example above are replaced with the ‘·’ symbol to make them visible.

Switch expression (standard feature)

See https://www.techiediaries.com/java/java-14-13-switch-expressions-example/

Note: It is preview feature in Java 12 and Java 13.

Note: In Java 13 there was big breaking change, see http://sandny.com/2019/11/17/java-13-switch-expressions/ so the code will look differently on Java 12.

In enhanced switch expression the entire switch block “gets a value” that can then be assigned to a variable in same statement.

Let’s consider classic switch example:

Note: The break statements ensures that the next block in the switch statement is not executed.

This code will print out “August” (in line 4 we’re assigning 8 to month).

Using a new syntax this can be rewritten as:

Note:

  1. It uses the -> operator instead of the colon
  2. We don’t need the break statement to stop the execution from flowing to the next cases.
  3. We can assign the switch expressions to variables or place them wherever expressions are expected in your Java code.

Java 14 also introduced a new yield statement to yield a value which becomes the value of the enclosing switch expression.

For example,

Look on default clause. It was modified in two ways. No, this is code block, so curly braces are required. In order to “return” value from the default clause new reserved word “yield” is used.

A return statement returns control to the invoker of a method (§8.4, §15.12) or constructor (§8.8, §15.9) while a yield statement transfers control by causing an enclosing switch expression to produce a specified value.

Note: In Python yield is used in generators (this is a subset of more general coroutines concept). yieldis a keyword in Python that is used to return from a function without destroying the states of its local variable and when the function is called, the execution starts from the last yield statement. This has nothing to do with Java’s yield.

For some additional look on:

Foreign Memory Access API in Java 14

https://www.baeldung.com/java-foreign-memory-access

Quote from link above:

Before the introduction of the foreign memory access API in Java, there were two main ways to access native memory in Java. These are java.nio.ByteBuffer and sun.misc.Unsafe [and from Java 9 java.lang.invoke.VarHandle see below] classes.

Note: In the bracket this my addition.

Moreover,

  1. AtomicInteger is still implemented using Unsafe (that was moved to jdk.internal.misc.Unsafe). It has interesting comment:
This class intended to be implemented using VarHandles, but there are unresolved cyclic startup dependencies.

AtomicReference was refactored to use VarHandle

Part of the purpose for VarHandles is to replace operations in sun.misc.Unsafe with a safe equivalent.

https://stackoverflow.com/questions/43558270/correct-way-to-use-varhandle-in-java-9/43561666#43561666

2. SequenceLayout is C-style array of the same type stored in memory.

3. GroupLayout is C-style struct or union stored in memory.

Guide to the @Serial Annotation in Java 14

https://www.baeldung.com/java-14-serial-annotation

Quote from link above:

Similarly to @Override, this annotation is used in combination with the serial lint flag to perform compile-time checks for the serialization-related members of a class.

A Guide to jpackage in Java 14

https://www.baeldung.com/java14-jpackage

Quote from link above:

jpackage is a command-line tool to create native installers and packages for Java applications.

It’s an incubating feature under the jdk.incubator.jpackage module.

Quote from link above:

In essence, JEP 358 aims to improve the readability of NullPointerExceptions, generated by JVM, by describing which variable is null.

--

--

alex_ber
alex_ber

Written by alex_ber

Senior Software Engineer at Pursway

No responses yet