Java 18 to 21 Features
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
| Release | Notable additions | Read 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 ZGC | see below |
Java 21 in Detail
| Feature | What it gives you | Read more |
|---|---|---|
| Virtual threads | Millions of cheap threads for blocking work: Thread.ofVirtual(), Executors.newVirtualThreadPerTaskExecutor() | "Virtual Threads in Java" |
Pattern matching for switch | case Circle c ->, with when guards and case null | "Pattern Matching for Switch in Java" |
| Record patterns | Take a record apart in a pattern: case Point(int x, int y) -> | "Record Patterns in Java" |
| Sequenced collections | getFirst, getLast, reversed, addFirst on lists, deques and ordered sets and maps | "Sequenced Collections in Java" |
| Exhaustive switch over sealed types | No default needed; the compiler checks every case is handled | "Exhaustive Switch with Sealed Types" |
| Generational ZGC | Low-pause garbage collection with better throughput | |
| Key Encapsulation Mechanism API | Standard 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.
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());
}
}first: Tuition[student=Navika, amount=15000]
last: Tuition[student=Advitha, amount=25000]
reversed: [20000, 1040, 15000]
total: 36040
virtual? trueExplanation:
switch (fee)matches on the type, and record patterns likeTuition(var s, var amount)pull the fields out directly. Thewhenguard caps tuition at ₹20,000.- There is no
default: becauseFeeis sealed, the compiler knows these cases are complete. getFirst,getLastandreversedcome from the newSequencedCollectioninterface, whichListnow extends.newVirtualThreadPerTaskExecutorstarts one virtual thread per task. Here it is three tasks; it would cope just as well with a hundred thousand.
Practice
- Easy: Replace
fees.get(fees.size() - 1)style code in an earlier program withgetLast(), andCollections.reverseon a copy withreversed(). - Harder: Add
record Hostel(String student, int months) implements Feeand include it inpermits. CompileTour21and read the compiler error from theswitch. Then add the missing case.
Key Takeaways
Was this tutorial helpful?