A Quick Look at Features from Java 8 to 17

Getting Started

This page introduces new features added since Java 7 and looks at the major improvements in each Java version up to Java 15, released in autumn 2020. Java now fully supports lambda expressions, functional programming, type inference with var, immutable collections with simple constructors, and multiline strings. There are also new and interesting experimental features such as data classes (record) and sealed classes. Finally, this page explains the Java REPL, which is useful for working efficiently.

Java 8 (LTS)

Functional Programming

Java 8 added functional programming and lambda expressions as language features. The two core paradigms of functional programming are immutable values and the increased importance of functions. Data transformation processing passes through a pipeline. At each stage, input is received and mapped to new output. Functional programming can be used in Java’s Stream and Optional (null-safe monads).

Lambda Expressions

public static void main(String[] args) {

    Runnable runner = () -> { System.out.println("Hello Lambda!"); };
    runner.run(); //Hello Lambda!
}

Streams

In typical computer programs, it is common to use a list of values and frequently transform each value. Before Java 8, transformations had to use for loops, but now Stream can be used as follows.

Stream.of("hello", "great")
    .map(s -> s + " world")
    .forEach(System.out::println);
> hello world
> great world

The map function takes a lambda that is applied to every element of the stream.

Through transformations, streams operate on List, Set, and Map. Streams can remove most loops from code.

Optional

One common problem in Java was the Null Pointer Exception. To address this, Java introduced Optional. It is a monad that wraps references for both null and non-null cases. Updates can be applied to an Optional in the form of functions.

Optional.of(new Random().nextInt(10))
    .filter(i -> i % 2 == 0)
    .map(i -> "number is even: " + i)
    .ifPresent(System.out::println);
number is even: 6

In the example above, a random number is generated, wrapped in an Optional object, and printed only when it is even.

Date and Time API

Many convenient date-related classes were added, including LocalDate, LocalTime, LocalDateTime, DateTimeFormatter, and ZonedDateTime.

Java 9

JShell

Java finally introduced JShell, a REPL, starting with Java 9. With JShell, you can test Java code in preview form without writing and compiling a complete Java class. In other words, you can run one command at a time and immediately see the result. A simple example is shown below.

$ <JDK>/bin/jshell
jshell> System.out.println("hello world")
hello world

People familiar with interpreted languages such as JavaScript and Python had enjoyed REPLs for a long time, but Java did not have this feature until then. In JShell, you can define not only variables but also more complex entities such as multiline functions, classes, and loops. It also supports autocompletion, which is useful when you want to learn the exact methods provided by a particular Java class.

Factory Methods for Immutable Collections

Simple initialization of List had long been missing in Java, but it was finally added. Previously, you had to write the following.

jshell> List<Integer> list = Arrays.asList(1, 2, 3, 4)
list ==> [1, 2, 3, 4]

This was simplified as follows.

jshell> List<Integer> list = List.of(1, 2, 3, 4)
b ==> [1, 2, 3, 4]

This useful of(...) method is included in List, Set, and Map. It allows you to create simple immutable objects in a single line.

Java 10

Type Inference with var

Java 10 added the new var keyword, which allows you to omit the variable type.

jshell> var x = new HashSet<String>()
x ==> []

jshell> x.add("apple")
$1 ==> true

In the example above, the compiler can infer that the type of x is HashSet.

This feature is useful for reducing boilerplate code and improving readability. However, there are some limitations. var can only be used inside method bodies, and the compiler infers the type at compile time, so everything is still statically typed.

What is boilerplate? In computer programming, boilerplate or boilerplate code refers to code that is reused in many places with minimal changes and repeatedly takes a similar form.

Java 11 (LTS)

Running a Single Source File

Previously, when writing a simple Java program in a single file, you first had to compile the file with javac and then run it with java. In Java 11, both steps can be performed with a single command.

First, create a single source file, Main.java.

public class Main {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}

Now compilation and execution can be done in one step.

$ java ./Main.java
hello world

When you want to start a simple starter program or test only one Java class, this feature makes it easier to run a single source file.

Java 12

Switch Expressions

Java 12 introduced switch expressions. This section looks at how this expression differs from the previous switch statement.

The previous switch statement defines program flow.

jshell> var i = 3
jshell> String s;
jshell> switch(i) {
    ...>    case 1: s = "one"; break;
    ...>    case 2: s = "two"; break;
    ...>    case 3: s = "three"; break;
    ...>    default: s = "unknown number";
    ...> }
jshell> s
s ==> "three"

By contrast, the new switch expression returns a value.

jshell> var i = 3;
jshell> var x = switch(i) {
    ...>    case 1 -> "one";
    ...>    case 2 -> "two";
    ...>    case 3 -> "three";
    ...>    default -> "unknown number";
    ...> };
x ==> "three"

Notice that this new switch expression is a kind of mapping function. It has one input (i here) and one output (x here). This feature is actually related to pattern matching, and it helps make Java compatible with functional programming principles. Similar switch statements have long been available in Scala.

There are a few points to note.

  • Use the arrow -> instead of a semicolon (;).
  • break is not required.
  • If all possible cases are covered, default can be omitted.
  • In Java 12, use --enable-preview -source 12 to use this feature.

Java 13

Multiline Strings

Have you ever defined a long multiline string such as JSON or XML? Until now, everything had to be placed on one line with newline characters \n, which made strings difficult to read. To solve this inconvenience, Java 13 introduced multiline strings.

public class Main {
    public static void main(String [] args) {
        var s = """
            {
                "recipe": "watermelon smoothie",
                "duration": "10 mins",
                "items": ["watermelon", "lemon", "parsley"]
            }""";
        System.out.println(s);
    }
}

Run the Main method with single-file startup.

java --enable-preview --source 13 Main.java
{
    "recipe": "watermelon smoothie",
    "duration": "10 mins",
    "items": ["watermelon", "lemon", "parsley"]
}

The resulting string spans multiple lines, quotation marks "" are preserved, and tabs \t are also preserved.

Java 14

Data Class record

Among the new features on this page, this is probably the most exciting one. Data classes finally appeared in Java. A class declared with the record keyword has automatic getters, a constructor, equals methods, and more. In other words, a large amount of boilerplate code can be removed.

jshell> record Employee (String name, int age, String department) {}
| created record Employee

jshell> var x = new Employee("Anne", 25, "Legal");
x ==> Employee[name=Anne, age=25, department=Legal]

jshell> x.name()
$2 ==> "Anne"

Scala has similar case classes, and Kotlin has data classes. In Java, many developers had used Lombok, which inspired the Java 14 record feature and provides many similar capabilities. For more details, see Baeldung.

instanceof Without Casting

The instanceof keyword already existed in earlier versions of Java.

Object obj = new String("hello");
if (obj instanceof String) {
    System.out.println("String length: " + ((String)obj).length());
}

The inconvenient part here is that you first check whether the obj variable is of type String, then cast it again to return the length.

In Java 14, after an instanceof check, the compiler can automatically infer the type and use it directly.

Object obj = new String("hello");
if (obj instanceof String mystr) {
    System.out.println("String length: " + mystr.length());
}

Java 15

Sealed Classes

Using the sealed keyword, you can restrict which classes can extend a particular class or interface.

public sealed interface Fruit permits Apple, Pear {
    String getName();
}

public final class Apple implements Fruit {
    public String getName() { return "Apple"; }
}

public final class Pear implements Fruit {
    public String getName() { return "Pear"; }
}

How does this source code help us? We now know how many kinds of Fruit there are. This is an important step toward fully supported pattern matching. Classes can be handled like enums. This sealed feature works well with the new switch expressions described earlier.

Java 16

  • Released on March 16, 2021.
  • Vector API: an automatic vector API that supports automatic parallel processing was added.
  • OpenJDK source code became available on GitHub.
  • Supports Metaspace instead of PermGen, which was removed in Java 8.
    • Reduces cost by quickly returning unused HotSpot class-metadata memory to the OS.

Java 17 (LTS)

  • Released on September 15, 2021.

Extra: Update Conditions for Java 8 and Later

The final topic on this page is licensing. You may have heard that Oracle stopped updating Java 8, the free commercial version. The options are as follows.

  • Use a newer Oracle JDK version. Oracle provides free security updates only for six months after each release.
  • Use an older JDK version at your own risk.
  • Use an older OpenJDK Java version. It continues to receive security updates through the open source community and partners.
  • Pay Oracle for premium support. For example, Java 8 is supported until 2030.

The following is the temporary Oracle support period by JDK.

JDK Release date Premier support until Extended support
7 2011.07 2019.07 2020.07
8 2014.03 2019.07 2020.07
9 2017.09 2018.03 -
10 2018.07 2018.09 -
11(LTS) 2018.09 2023.09 2026.09
12 2019.03 2019.09 -
13 2019.09 2020.03 -
14 2020.03 2020.09 -
15 2020.09 2021.03 -
Oracle support schedule by JDK

Oracle’s new licensing model is affected by the new release cycle. Oracle releases a new Java version every six months. This helps Oracle improve Java at a fast pace, get feedback more quickly through experimental features, and catch up with modern languages such as Scala, Kotlin, and Python.

For more information about licensing, refer to this article.

Closing

Java has come a long way over the past six years, and there have actually been eight new Java releases since then. All these useful new features help make Java a competitive option compared with other JVM-based competitors such as Scala and Kotlin.

References