In honor of the release of Java 19, we present this series of blog posts on how to use the latest _bleeding edge_ features available in the Java programming language. Here at Starburst, Java is our primary language. Trino is written in Java as are most of our backend services. Unlike many companies, Starburst is not stuck on an older version of Java.
Trino, in fact, was recently updated to require Java 17. The Java language has evolved so dramatically since its inception that code written 20 years ago hardly resembles modern code written today.
Many of the Java libraries we use today were written a long time ago and must also support old versions of Java lest they wreak havoc with their user communities. Imagine if you could rewrite some of your favorite libraries with a fresh usage of modern Java? In this series, we do exactly that. The goal is not to replace existing libraries – longstanding libraries shouldn’t be replaced merely for aesthetic reasons. Our purpose is take a familiar use-case and see how it might be implemented using the very latest, even experimental, features in Java.
Let’s take a common use-case that nearly all Java developers have to deal with – JSON. The standard library for JSON in Java is Jackson. It would be futile to attempt to emulate the performance and features of Jackson. Instead, let’s have a simple goal of being able to write a Java object into valid JSON text and then read valid JSON text and map it to a Java object, so serializing and deserializing.
In this series you can learn about numerous modern Java features:
- Records – effectively use Java records for concise code that eliminates boilerplate as well as enabling pattern matching and de-structuring
- Sealed interfaces – close a hierarchy of related classes to express a pre-defined set of related types
- Interfaces as pseudo companion classes – a Java
classdefinition is a low level concept and is usually not necessary at the top level
- Record patterns – records are not just data carriers. They enable functional idioms that in the past were only available in languages such as Scala and Haskell.
- Pattern matching for switch – the union of record patterns and switch makes code much easier to reason about and to write
In addition, here are some goals and design decisions:
- No magic – no hacks or tricks should be used. Only standard, modern Java.
- No annotations – serialization libraries have tended to rely on Java’s annotation processing but this can be error prone and difficult to maintain and debug
- No special libraries or hacks for type information – surprisingly, standard Java retains enough type information to write a serialization library that supports generics
- No dependencies – the library should be self-contained and not rely on any third party libraries
This series is targeted at Java developers who are comfortable with Java 8, but have not used or are not comfortable with newer features in Java. The code samples in this series are not meant for production, but we would appreciate any bug reports. A more full-featured version of the JSON library is available on GitHub.
- Designing a JSON model – Use some newer features of Java to model the JSON RFC.
- Serialization – Serializing is simpler than deserializing. Let’s start with that.
- Printing – Converting a stream of JSON tokens into JSON text.
- Parsing – Parsing JSON text into tokens.
- Deserialization – The harder part, deserialize JSON into Java objects.
- Deserialization of complex types – Finalizing the system into a usable, extensible library.
- Conclusion – Summarize what we achieved together.
Want to be able to use the latest features of Java? We’re hiring!