Using Java 8 in late 2020

Nuno Caro
5 min readJan 24, 2021
Photo by Markus Spiske on Unsplash

A long time has passed since Java 8 release, back in 2014, and a lot has changed in the meantime. New frameworks were built, new programming languages were created and some of the existing ones have been adapted to better fit modern developer requirements.

Java was no exception to this trend. In the last years, the Java language has been the target of several additions, that have been requested by developers for quite some time. Some of these are record classes, variable type inference, sealed classes, pattern matching, etc. All of the above fall into the scope of project amber.

While this is a welcome change for developer satisfaction, in general, they are only available in different language and bytecode levels, meaning that they won’t be available for everyone without a JDK update. Unfortunately, several reports show that Java 8 is still the most used JDK version in production.

The point of this article is to shed some light on what is available for people that are stuck in a Java 8 world but want to have something similar to new language features.

Project Lombok

Let's start with one you probably already know. Project Lombok is a tool that plugs into the compilation process and allows you to express additional semantics into plain Java constructs through annotations.

This tool provides a lot of useful features, but I’d like to highlight 3 of them, that removes most of my boilerplate away: Value, EqualsAndHashCode, and ToString.

EqualsAndHasCode and ToString do exactly what they say. They provide sensible implementations for these methods based on the internal class state.

Value classes are plain immutable data holders, much like records in later Java releases. Classes annotated with this will automatically have their state private and final with public getters, besides automatically implementing equals, hashCode and toString.

Lombok example

The one concern I have with this project is that it relies on private compiler APIs and requires a plugin to be compatible with your IDE. While the compiler will remain mainly stable, IDEs change all the time and this may become a pain to manage. Pay special attention to this when you decide to use it.

AutoValue

AutoValue makes part of a set of annotation processors that generate code for different purposes. This one in particular aims to generate immutable data classes for a specific interface or abstract class.

This is, in essence, the same as Lombok's value classes, but with generated code, instead of compiler manipulation, which makes it completely portable. It offers the same end result but is a bit more verbose and sometimes feels clunky.

AutoValue usage example

Github page

java-compiler-extensions

As a disclaimer, this is a shameless plug into some of my work, but I’ve found this really useful in my day to day life as a programmer.

This project provides 2 annotation processors, for now, one for enum expressions and another for sealed classes. The main goal is to enable us to write code in a way so that the compiler can detect breaking changes.

Enum expressions is a compiler feature that will check the exhaustiveness of the expression to make sure we didn’t forget to handle any branch. This annotation processor automatically generates methods that serve the exact same purpose. If the enum changes shape and adds a new value, the compiler will complain whenever that value isn’t handled. Better explained with an example:

Enum expression example

Sealed classes are class hierarchies whose full inheritance chain is known at compile time. Again, the compiler can be prepared to validate the exhaustiveness of the code to avoid errors.

The processor that enables this style of code and enforces some limitations to provide better security. The generated code is similar to enum expressions because it allows us to write code that will not compile if breaking changes are made into the hierarchy.

To use this annotation processor we must provide a base class with a package-private constructor. Then mark every inheritor we want to expose with a SealedType annotation. The code generator does the rest.

Example for sealed class expression

For additional configuration on these processors, check out the GitHub page.

Other additional goodies

Async-await, or its variants, are proven to be a much more friendly way to deal with asynchronous code than callback-based APIs. As we all know, Java doesn’t provide anything to help up with this, in fact, they most likely will not this at the language level because of project Loom.

Fortunately, the guys at EA (yes, the game developer) have come up with a very interesting solution called ea-async.

The way this works is through bytecode instrumentation at either build or runtime by a java agent. The end result is very clean code as we can see by the samples extracted from their Github page.

This project solves awaiting for futures and even handles exceptions on error cases.

I have never tried it in production, rather just played around with, but I liked it very much.

Extension methods is another feature provided by a lot of modern languages that I really miss in java. Unfortunately, this one does not seem to have any official solution, but there are some projects that do provide similar features.

One of those projects is Manifold extensions, which attempts to simulate the feature using both compiler manipulation and IDE plugins to work seamlessly. Again this will hurt portability but may end up saving you from a lot of boilerplate.

The other one I know is Lombok (the same as above). I haven’t mentioned it because the feature is still experimental.

I hope this article will help people that are stuck in older Java versions to improve their code base and reduce errors commonly associated with boilerplate.

--

--

Nuno Caro

Computer science geek with a splash of party animal.