1. Introduction
Java 21 is a Long-Term Support (LTS) version of Java that was officially released on September 19, 2023. The Java Specification Request(JSR) 396 provides a detailed view of the enhancements in this new version of Java. In this tutorial, you will learn the main features of the JDK 21. We will not cover any preview feature in this article.
2. Record Patterns
Java 17 introduced the Record type as a way to simplify the implementation of data-carrier classes. Any code using a Record class can use the automatically generated accessors to retrieve the field values.
//Use of record before Java 21
if(object instanceof Student student){
String firstName = student.firstName();
String lastName = student.lastName();
System.out.println(firstName +" " +lastName);
}
Java 21 has provided an enhancement to the previous code. The compiler uses pattern matching to automatically bind the record components to variables.
//Use of record with Java 21
if(object instanceof Student(String firstName, String lastName)){
System.out.println(firstName +" " +lastName);
}
Read more about Record Patterns(JEP 440).
3. Pattern Matching for switch
Prior to this version, the switch construct only allowed a limited number of Java types in a case label(integral primitive types, enum, String). In Java 21, you can use complex patterns in switch labels.
//Prior to Java 21
private void switchBeforeJava21(Object object){
if(object == null) {
System.out.println("I'm null");
}else if(object instanceof Integer i){
if(i % 2 == 0)
System.out.println("I'm even");
else
System.out.println("I'm odd");
}else if(object instanceof String s){
System.out.println("I'm a String : "+s);
}else{
System.out.println("I'm a different object");
}
}
//With Java 21
private void switchWithJava21(Object object){
switch (object){
case null -> System.out.println("I'm null");
case Integer i when i%2 == 0 -> System.out.println("I'm even");
case Integer i when i%2 == 1 -> System.out.println("I'm odd");
case String s -> System.out.println("I'm a String : "+s);
default -> System.out.println("I'm a different object");
}
}
Read more about Pattern Matching for Switch (JEP 441).
4. Sequenced Collections
Java 21 has introduced three new interfaces SequencedCollection
, SequencedSet
, and SequencedMap
. A SequencedCollection
is a collection whose elements have a predefined encounter order. Thus, the collection has a first and last element. Each element in the collection has a predecessor and a successor. The collection also supports operations at each end (first and last element). A SequencedSet
is a SequencedCollection
that does not support duplicates. A SequencedMap
is a Map whose entries have a predefined encounter order.
4.1. Sequenced List
Java has updated the List
hierarchy. The List
interface extends SequencedCollection
. Hence, a List is by default a SequencedCollection
.
private void sequencedList(){
System.out.println("*****Sequenced List*****");
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
String firstItem = list.getFirst();
String lastItem = list.getLast();
List<String> reversedList = list.reversed();
System.out.println(firstItem); //Hello
System.out.println(lastItem); // World
System.out.println(reversedList); //[World, Hello]
}
4.2. Sequenced Set
Unlike lists, not all sets are sequenced sets. Therefore, you cannot use the Set interface directly to create a SequencedSet.
private void sequencedSet(){
System.out.println("*****Sequenced Set*****");
SequencedSet<String> set = new LinkedHashSet<>();
set.add("Hello");
set.add("World");
String firstItem = set.getFirst();
String lastItem = set.getLast();
SequencedSet<String> reversedSet = set.reversed();
System.out.println(firstItem); //Hello
System.out.println(lastItem); // World
System.out.println(reversedSet); //[World, Hello]
}
4.3. Sequenced Map
Just like sets, not all maps are sequenced maps. HashMap
is not a sequenced map, LinkedHashMap
on the contrary, it is.
private void sequencedMap(){
System.out.println("*****Sequenced Map*****");
SequencedMap<String,String> map = new LinkedHashMap<>();
map.put("Hello","Hello");
map.put("World","World");
Map.Entry<String,String> firstItem = map.firstEntry();
Map.Entry<String,String> lastItem = map.lastEntry();
SequencedMap<String,String> reversedMap = map.reversed();
System.out.println(firstItem); //Hello=Hello
System.out.println(lastItem); //World=World
System.out.println(reversedMap); //{World=World, Hello=Hello}
}
Read more about Sequenced Collections(JEP 431).
5. Virtual Threads
Java 21 introduces virtual threads, a lightweight thread type that simplifies writing and maintaining high-throughput applications. While it requires around 1Mb to create a traditional thread(or platform thread), a virtual thread only requires around 1Kb of memory.
The Thread
class is enriched with new methods:
Thread thread = Thread.ofVirtual().start(() -> {
System.out.println("Hello from a virtual thread!");
});
System.out.println(thread.isVirtual());//True
You can also use the ExecutorService
to create a large number of virtual threads:
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 1_000_000).forEach(i ->
executor.submit(() -> {
// Simulate a task
System.out.println("Task " + i + " running");
})
);
}
Important: Please note that virtual threads are not meant to replace platform threads. Their goal is to provide a way to easily scale high-throughput applications.
Read more about Virtual Threads(JEP 444).
6. Key Encapsulation Mechanism API
The Key Encapsulation Mechanism(KEM) API is a new feature of Java 21, designed to help developers build post-quantum cryptographic protocols efficiently. The goal of KEM API is to strengthen the exchange of the symmetric encryption key in the context of Public Key Infrastructure (PKI). A new class javax.crypto.KEM
is introduced to handle the encapsulation and decapsulation processes.
// Receiver side
KeyPairGenerator g = KeyPairGenerator.getInstance("X25519");
KeyPair kp = g.generateKeyPair();
publishKey(kp.getPublic());
// Sender side
KEM kemServer = KEM.getInstance("DHKEM");
PublicKey publicKeyReceiver = retrieveKey();
KEM.Encapsulator e = kemServer.newEncapsulator(publicKeyReceiver);
KEM.Encapsulated enc = e.encapsulate();
SecretKey sharedSecretSender = enc.key();
byte[] keyEncapsulationMessage = enc.encapsulation();
//...The key encapsulation message should be sent to the receiver here
// using for example a method like sendBytes(keyEncapsulationMessage)
// Receiver side
// ...The receiver should receive the key encapsulation message here
// using for example a method like receiveBytes()
byte[] em = keyEncapsulationMessage;
KEM kemR = KEM.getInstance("DHKEM");
KEM.Decapsulator d = kemR.newDecapsulator(kp.getPrivate());
SecretKey sharedSecretReceiver = d.decapsulate(em);
// sharedSecretSender and sharedSecretReceiver will be identical
assert Arrays.equals(sharedSecretSender.getEncoded(),sharedSecretReceiver.getEncoded());
Read more about Key Encapsulation Mechanism API(JEP 452).
7. Conclusion
In this article, you learned the main features introduced by Java 21.
You can find the complete code of this article here in GitHub