Java 21: Die wichtigsten Features seit Version 17
Java 21, die neue Version mit verlängertem Support, ist da! Hier stelle ich die wichtigsten Features vor:
- Interface SequencedCollection,
- Record Patterns,
- Neuerungen bei switch und
- das Highlight virtuelle Threads
Falls Ihr noch Java 11 verwendet, schaut euch hier die Features von Java 17 an: java-17-features.html
Interface SequencedCollection
// Mutable list created.
List<String> list = new ArrayList<>(List.of("1st", "2nd", "3rd"));
log.info("Read first & last element in list: %s & %s"
.formatted(list.getFirst(), list.getLast()));
var reversedList = list.reversed();
reversedList.addFirst("4th");
log.info("Reversed order with new first element: " + reversedList);
log.info("Changed list: " + list);
list.removeFirst();
list.removeLast();
list.addFirst("begin");
list.addLast("end");
log.info("Changed list: " + list);
Log-Ausgabe zum Code:
INFO: Read first & last element in list: 1st & 3rd
INFO: Reversed order with new first element: [4th, 3rd, 2nd, 1st]
INFO: Changed list: [1st, 2nd, 3rd, 4th]
INFO: Changed list: [begin, 2nd, 3rd, end]
Trotz der komfortablen Methoden der SequencedCollection kann es in folgenden Fällen zu RuntimeExceptions kommen:
- NoSuchElementException - wenn die Liste leer ist und ihr das erste oder letzte Element lesen oder entfernen wollt.
- UnsupportedOperationException - wenn die Liste unveränderlich ist und ihr das erste oder letzte Element entfernen oder hinzufügen wollt. Eine unveränderliche Liste wird z. B. so erzeugt List.of("test"). Im Code-Beispiel erzeuge ich daher eine veränderliche ArrayList.
- NullPointerException - wenn die Listenimplementierung das Hinzufügen von null nicht erlaubt.
Pattern Matching bei switch
BaseStream stream = DoubleStream.of(1.1);
switch (stream) {
case null -> log.info("null is now a possible case.");
case IntStream is when is.isParallel() ->
log.info("Expression in case.");
case DoubleStream ds -> log.info("Casted in case.");
default -> throw new IllegalStateException();
}
Record Patterns
record Pair(String key, Integer value){}
record Single(Double element){}
private static void recordPatterns() {
var mixedList = List.of(
new Single(1.1),
new Pair("2nd", 2));
for (Object entry : mixedList) {
if (entry instanceof Pair(String k, Integer v))
log.info("Record has: %s %d".formatted(k, v));
switch (entry) {
case Single s -> log.info("Record: " + s);
case Pair(String k, Integer v) -> log.finest("...");
default -> throw new IllegalStateException();
}
}
}
Virtuelle Threads
try (var executorService = Executors.newVirtualThreadPerTaskExecutor()) {
executorService.submit(() -> {
var virtualThread = Thread.currentThread();
log.info(virtualThread.threadId() + " : "
+ virtualThread.isVirtual());
});
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
- Die Log-Ausgabe zum Code sieht so aus:
INFO: 22 : true - Mit der neuen Methode isVirtual überprüfen wir, ob wirklich ein virtueller Thread erzeugt wurde. Der hier gezeigt Thread loggt nur seine eigene ID und ob er virtuell ist.
- Zum Erzeugen neuer Threads wird ein ExecutorService mit Executors.newVirtualThreadPerTaskExecutor instanziiert. Dieser Service erzeugt neue virtuelle Threads mit der Methode submit. Somit können virtuelle Threads analog zu klassischen Thread erzeugt werden - am ExecutorService Interface hat sich nichts geändert.
- Das hier verwendete try-with bedient das AutoCloseable Interface, um den ExecuterService automatisch zu schließen.
Fazit
2 Jahre nach Java 17 gibt es ein neues LTS (Long Term Support) Release. Dieses erweitert die Sprache Java um wenige, aber gute Sprach-Features. Virtuelle Threads sind für mich das Highlight. Sie helfen unter der Last vieler, parallel eingehender Requests eine bessere Hardware-Nutzung zu erreichen - das spart Geld und Strom!
Kommentare