Java 18 to 21 Features

Naveen AnamaneniUpdated · 5 min read

A school that can only run one class per teacher hits a limit quickly: more students means hiring more teachers. Imagine instead that a teacher could hand a class a worksheet, step out to help another class while they work, and come back exactly when needed. Suddenly a few teachers can run hundreds of classes. That is the idea behind virtual threads, the headline of Java 21 (September 2023, LTS). Together with pattern matching for switch, record patterns and sequenced collections, it made Java 21 the most important release since Java 8. In this lesson, you will get a quick reference to Java 18, 19, 20 and 21, with a tour program.

What Each Release Added

ReleaseNotable additionsRead more
Java 18 (Mar 2022)UTF-8 by default for files and text everywhere; a simple web server (jwebserver); @snippet in Javadoc; finalization deprecated for removal
Java 19 (Sep 2022)Previews of virtual threads and record patterns; structured concurrency incubator
Java 20 (Mar 2023)More previews; scoped values incubator
Java 21 (Sep 2023, LTS)Virtual threads, record patterns, pattern matching for switch and sequenced collections all final; generational ZGCsee below

Java 21 in Detail

FeatureWhat it gives youRead more
Virtual threadsMillions of cheap threads for blocking work: Thread.ofVirtual(), Executors.newVirtualThreadPerTaskExecutor()"Virtual Threads in Java"
Pattern matching for switchcase Circle c ->, with when guards and case null"Pattern Matching for Switch in Java"
Record patternsTake a record apart in a pattern: case Point(int x, int y) ->"Record Patterns in Java"
Sequenced collectionsgetFirst, getLast, reversed, addFirst on lists, deques and ordered sets and maps"Sequenced Collections in Java"
Exhaustive switch over sealed typesNo default needed; the compiler checks every case is handled"Exhaustive Switch with Sealed Types"
Generational ZGCLow-pause garbage collection with better throughput
Key Encapsulation Mechanism APIStandard API for modern key exchange

Unnamed variables (_) and the simpler void main() were previews in Java 21 and became final later (Java 22 and Java 25).

A Java 18 to 21 Tour

Let's see a program that uses the four Java 21 headline features.

Tour21.java▶ Try it
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
 
public class Tour21 {
    sealed interface Fee permits Tuition, Transport {}
    record Tuition(String student, int amount) implements Fee {}
    record Transport(String student, int km) implements Fee {}
 
    static int amountOf(Fee fee) {
        return switch (fee) {                                   // patterns
            case Tuition(var s, var amount) when amount > 20000 -> 20000;
            case Tuition(var s, var amount) -> amount;          // record
            case Transport(var s, var km) -> 800 + km * 20;
        };                                                      // exhaustive
    }
 
    public static void main(String[] args) throws Exception {
        List<Fee> fees = new ArrayList<>(List.of(
                new Tuition("Navika", 15000),
                new Transport("Srihitha", 12),
                new Tuition("Advitha", 25000)));
 
        System.out.println("first: " + fees.getFirst());       // sequenced
        System.out.println("last:  " + fees.getLast());
        System.out.println("reversed: " + fees.reversed().stream()
                .map(Tour21::amountOf).toList());
 
        try (var pool = Executors.newVirtualThreadPerTaskExecutor()) {
            List<Future<Integer>> results = new ArrayList<>();
            for (Fee f : fees) {
                results.add(pool.submit(() -> amountOf(f)));    // virtual
            }
            int total = 0;
            for (Future<Integer> r : results) {
                total += r.get();
            }
            System.out.println("total: " + total);
        }
        System.out.println("virtual? " + Thread.ofVirtual()
                .unstarted(() -> {}).isVirtual());
    }
}
Output
first: Tuition[student=Navika, amount=15000]
last:  Tuition[student=Advitha, amount=25000]
reversed: [20000, 1040, 15000]
total: 36040
virtual? true

Explanation:

  1. switch (fee) matches on the type, and record patterns like Tuition(var s, var amount) pull the fields out directly. The when guard caps tuition at ₹20,000.
  2. There is no default: because Fee is sealed, the compiler knows these cases are complete.
  3. getFirst, getLast and reversed come from the new SequencedCollection interface, which List now extends.
  4. newVirtualThreadPerTaskExecutor starts one virtual thread per task. Here it is three tasks; it would cope just as well with a hundred thousand.

Practice

  1. Easy: Replace fees.get(fees.size() - 1) style code in an earlier program with getLast(), and Collections.reverse on a copy with reversed().
  2. Harder: Add record Hostel(String student, int months) implements Fee and include it in permits. Compile Tour21 and read the compiler error from the switch. Then add the missing case.

Quick check: Java 18 to 21

4 questions
1Which release made UTF-8 the default charset?
2Which features became final in Java 21?
3Why does the switch in Tour21 need no default case?
4What do virtual threads make cheap?

Key Takeaways

Was this tutorial helpful?

Lesson 564 of 578