Java 8 to Java 15 in 10 minutes

This post is translation of the article with some clarifications.

In this article, I want to take a look at the main features added in Java from version 7 to version 15. I will touch on at least one major improvement for each version, up to Java 15, which, incidentally, was released in the fall of 2020.

Java now supports lambda expressions, functional programming, variable declaration through var, immutable collections with simple constructors and “multi-line” strings (Multi-line Strings). In addition, there are new interesting experimental features such as date classes (record) and sealed classes. Finally, I will talk about Java REPL (a very handy thing for quick experiments). So, let’s begin.

Functional Programming (Java 8)

In Java 8, functional programming and lambdas were added as new features of the language. The bottom line is simple: the data is broken down into steps, each of which takes some input signal and transforms it into a new output signal. Functional programming can be used with data streams ( Stream) and null-safe data (Optional)… Consider an example below.

Stream (Java 8)

Very often, while writing a program, you have to work with arrays and sheets. Namely, with their transformation, filtering, aggregations, etc. Before Java 8, you had to use a loop for such operations. forbut now you can use streams like this:

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

Function map accepts a lambda as input, which will be applied to all elements of the stream. Streams can work with List, Set and Map (via transformation). Thanks to threads, you can get rid of almost all loops in your code!

Optional (Java 8)

Another common problem in Java was Null Pointer Exceptions… To solve this problem, appeared Optional… Essentially it is a wrapper for an object that can be null… Applying updates to Optional can be done in a functional way:

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 above snippet, we create a random number, wrap it inside an object Optional, we filter and leave only even numbers and at the end, if the object is not null, display it on the screen.

JShell (Java 9)

Finally we have a REPL for Java, and its name is – JShell! In a nutshell: JShell lets you experiment with Java snippets without writing and compiling a complete class. Instead, you can execute one command at a time and see the result immediately. Here’s a simple example:

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

People familiar with interpreted languages ​​(JavaScript or Python) have had the ability to use the REPL for a long time, but until now this feature was not available in Java. JShell allows you to define variables as well as more complex entities such as multi-line functions, classes, and loops. What’s more, JShell supports auto-completion, which is great if you don’t know the exact methods that a given Java class provides.

Factory Methods for Immutable Collections (Java 9)

Simple initialization of lists has been absent in Java for a long time, but now those days are over. Previously, you had to do something like this:

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

This is now simplified like this:

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

Method of(...) exists for List, Set and Map… They all create an immutable object with just one simple line of code.

Var keyword (Java 10)

Java 10 has a new keyword var, which allows you not to specify the type of the variable.

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

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

In the above snippet, the compiler can determine the type x how HashSet… This feature helps to shorten very long variable names and improve readability. However, there are some limitations here: you can use var only inside the body of the method, and the compiler will determine the type at compile time, so everything is still statically typed.

Single Source File Launch (Java 11)

Previously, when writing a simple program into one file, you had to first compile the file using javac and then run it using java. Starting from version 11, you can perform both actions with one command. First, we define our only source file Main.java

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

Compile and run

$ java ./Main.java
hello world

For simple programs or experiments with just one class, this function of running single source files is quite useful.

Switch expressions (Java 12)

Java 12 brought us Switchexpressions. Here’s a quick demonstration of how the expression differs from the old version.

Old switch statement:

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"

The new method allows you to do the following:

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

A similar operator has been around for a long time in Scala, Kotlin. Java decided to keep up. There are a few things to note:

  1. arrows are used instead of double dots ->

  2. no need for break

  3. defaultmay be omitted when all possible cases have been considered.

  4. To enable this feature in Java 12 use --enable-preview --source 12

Multi-line Strings (Java 13)

Have you ever had to define a long multi-level string like JSON or XML? So far, you’ve probably compressed everything into one line and used newlines nwhich makes it much less readable. Starting from version 13, you can do this:

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 and get:

java --enable-preview --source 13 Main.java

{
    "recipe": "watermelon smoothie",
    "duration": "10 mins",
    "items": ["watermelon", "lemon", "parsley"]
}

All spaces and tabs are preserved.

Date classes: record (Java 14)

Of all the new features described in this article, this is perhaps the one that I am most pleased with: finally, date classes appeared in Java! Declared using a keyword record and have automatic getters, constructor, method equals() etc. In short, you can get rid of a huge chunk of boilerplate!

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 a similar capability with case classesand Kotlin with data classes… In Java, many developers have used Lombokwhich offered pretty much all of the capabilities that are currently inspiring writing for Java 14.

instanceof without type hello (Java 14)

Previous versions of Java already contained the keyword instanceof:

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

But the downside is that even if we are convinced of the type, we still need to explicitly cast it. Now, in Java 14, the compiler is smart enough to automatically detect the type after checking instanceof:

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

Sealed classes (Java 15)

Using the keyword sealed you can restrict which classes can extend a class or interface. Here’s an example:

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"; }
}

Bonus: Updated licensing terms starting with Java 8

The final topic for this article is licensing. Most of you have heard that Oracle has discontinued updates to Java 8 (for the free commercial version). So here are your options:

  1. Use a newer version of Oracle JDK (Oracle only offers free security updates for 6 months after each release).

  2. Use the old JDK version at your own risk

  3. Use an older version of OpenJDK Java that is still receiving security updates from the open source or third party community.

  4. Pay Oracle for prime support (e.g. Java 8: support until 2030). Below are the tentative Oracle support dates for each JDK:

Oracle’s new licensing model is influenced by a new release cycle: Java will be updated every 6 months. This approach helps Oracle improve the language faster, get feedback faster through experimental features, and catch up with more modern languages ​​such as Scala, Kotlin, and Python.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *