Java 15 schiebt Textblöcke und Records weiter in Richtung Produktionsreife und sichert die Erbfolge bei Klassen gegen den “Enkeltrick” ab.
Ein Jahr Zeit bleibt noch bis zum nächsten Langzeit-Release von Java. Genügend Zeit also, um neue Features einzuführen, die Java-Programmierern die Arbeit erleichtern. Zugleich räumten die Entwickler beim aktuellen Java 15 erstmals in größerem Stil im Code auf und schnitten alte Zöpfe ab.
Seit Mitte September 2020 steht Java 15 für die allgemeine Verwendung zum Download bereit [1]. Zu den Änderungen gehören eine bunte Mischung an Spracherweiterungen, Verbesserungen an der Laufzeitumgebung sowie Aufräummaßnahmen. Treiber der Entwicklung sind vor allem kleine, produktivitätssteigernde Ideen aus dem Project Amber [2].
Zu diesen arbeitserleichternden Details gehören beispielsweise die Textblöcke (JEP 378). Sie erlauben es, im Quelltext ganze Textbereiche mit enthaltenen Zeilenumbrüchen oder Anführungszeichen einzufügen. Der Quelltext ist dadurch gut zu lesen, ohne dass dafür die bisher nötigen Klimmzüge zu machen sind. Eine nette Automatik schneidet dabei die führenden Leerzeichen am Anfang und Ende eines Blocks so weg, dass der Entwickler durch eine Code-Einrückung nicht plötzlich einen anderen String erzeugt. Den Block hebt Netbeans nach Änderungen der Farbeinstellung hervor, IntelliJ blendet dezente Linien ein (Abbildung 1).

Abbildung 1: Textblöcke nehmen mehrzeiligen Text auf. IntelliJ (links der roten Linie) und Netbeans zeigen den Block schon beim Editieren an.
Alle weiteren Sprachänderungen befinden sich dagegen noch im Preview-Status. Sie lassen sich nur dann nutzen, wenn der Entwickler beim Kompilieren und Starten den Code-Level 15 und die Option »–enable-preview« angibt.
Auch Pattern Matching für »instanceof« geht auf Project Amber zurück und liegt nun in der zweiten und letzten Vorschauversion vor der allgemeinen Freigabe vor. Dabei lässt der Entwickler nach einem erfolgreichem Test das bereits gecastete Objekt einer Variablen zuweisen (Listing 1) und spart so einen separaten Cast.
Listing 1
Das neues instanceof
// Pattern Matching
Object zuUntersuchen = "Ein String";
// Alt
if ( zuUntersuchen instanceof String &&
((String) zuUntersuchen).contains("String")) {
[...]
}
// Neu, s enthält String
if ( zuUntersuchen instanceof String s &&
s.contains("String")) {
[...]
}
Ebenso in der zweiten Begutachtungsrunde befinden sich die Records (JEP 384). Sie verringern als kleine Brüder der Klassen den Schreibaufwand für Datentransferobjekte; ein kleines Beispiel zeigt Listing 2. Im einfachsten Fall geben Entwickler bei den Records nur die Variablen an, das Feature erzeugt dann Konstruktor, Zugriffsmethoden, »equals«, »hashCode« und »toString« automatisch. Viel mehr ist auch nicht möglich: Logik darf nur in den Canonical Constructor, weitere Methoden lassen sich nicht definieren. Die aktuelle Version integriert noch Feedback zum Umgang mit Interfaces und Annotations, mit Java 16 sollen die Records dann fertig sein.
Listing 2
Records
// Lokale Record-Definition
record Beispiel ( int x, int y){
}
Beispiel p1 = new Beispiel(3, 14);
Beispiel p2 = new Beispiel(3, 14);
System.out.printf("Record toString: %s%n", p1);
System.out.printf("Zugriff : %d%n", p1.x);
System.out.printf("eingebautes equals: %b%n", p1.equals(p2));
// Canonical Constructor zur Überprüfung
record BeispielMitÜberprüfung (int x, int y){
BeispielMitÜberprüfung {
if (x < y) { throw new IllegalArgumentException("x < y !"); }
}
}
// Wirft Fehler
new BeispielMitÜberprüfung(1, 2);
Enkeltrick verhindern
Das Sichtbarkeitskonzept von Klassen und Interfaces fällt bislang recht einfach aus: Einmal zur Vererbung freigegeben, darf jeder sie nutzen. Das ist nicht immer gewünscht, da so auch jeder über Vererbung oder die Wahl des Namensraums Zugriff auf Variablen und Methoden der Basisklasse erlangt. Andere Sprachen, wie zum Beispiel Express [3], brachten daher schon immer einen Mechanismus mit, um das Vererben auf bestimmte Klassen einzugrenzen.
Dank der Sealed Classes (JEP 360) klappt das nun auch in Java. Hier definiert der Entwickler über »permits« eindeutig, wer erben darf. Das gilt zwangsweise für den ganzen Stammbaum. Alle Zwischenklassen muss der Programmierer als »sealed« markieren, alle Blattklassen als »final« (Listing 3). Die neuen Wörter »permits« und »sealed« gelten dabei nicht als allgemeine Schlüsselwörter – wer sie bislang als Variablennamen eingesetzt hat, muss das nicht ändern.
Listing 3
Sealed-Klassen schränken Vererbung ein
sealed class ComparisonExpression
permits ComparisonEqual, ComparisonGreater, ComparisonNotSoGreat { }
// Alle Abkömmlinge müssen sealed oder final sein
final class ComparisonEqual extends ComparisonExpression { }
final class ComparisonGreater extends ComparisonExpression { }
sealed class ComparisonNotSoGreat extends ComparisonExpression
permits HasBeenBetter, HopeForNextYear { }
final class HasBeenBetter extends ComparisonNotSoGreat { }
final class HopeForNextYear extends ComparisonNotSoGreat { }
// Kompiliert nicht
// final class JustACheck extends ComparisonExpression { }
Ohne Umweg
Bei Cloud-Anwendungen kommen immer öfter Caching- oder In-Memory-Datenbank-Lösungen wie Memcached [4] oder Ignite [5] zum Einsatz. Deren Daten liegen bereits im Hauptspeicher vor, doch gibt es bislang keinen eleganten Weg, aus Java direkt darauf zuzugreifen. Kopieren verdoppelt den Speicherbedarf und kostet Zeit; Lösungen auf Basis von »sun.misc.Unsafe« oder JNI gefährden potenziell die JVM.
Die Foreign-Memory-Access-API (JEP 383) liegt nun als zweite Preview vor. Sie erlaubt den Lese- und Schreibzugriff auf Speichersegmente ohne die Seiteneffekte der bisherigen Lösungen.
Die in RFC 8032 definierten Edwards-Kurven sind ein moderner Signaturalgorithmus auf Basis von elliptischen Kurven. Nun gibt es zwar reichlich solcher Algorithmen, doch die Edwards-Kurven sind für TLS 1.3 vorgesehen. Damit Entwickler und Nutzer weiterhin HTTPS ohne Zusatzbibliotheken nutzen können, wurde der Crypto-Provider in JEP 339 um diesen Algorithmus erweitert. Wer ihn anwenden will, geht dabei so vor wie bei allen anderen Signaturalgorithmen (Listing 4).
Listing 4
Signieren mit elliptischen Kurven
byte[] data = "zu signieren".getBytes();
// Erzeuge Paar aus einem privaten und einem öffentlichen Schlüssel
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
KeyPair kp = kpg.generateKeyPair();
// Signatur erstellen
Signature sig = Signature.getInstance("Ed25519");
sig.initSign(kp.getPrivate());
sig.update(data);
byte[] dieSignatur = sig.sign();
// Signatur überprüfen
Signature check = Signature.getInstance("Ed25519");
check.initVerify(kp.getPublic());
check.update(data);
boolean passt = check.verify(dieSignatur);
Müllsammler
Ein großer Vorteil von Java gegenüber C und C++ ist, dass es ungenutzten Speicher automatisch freigibt. Das gibt es nicht zum Nulltarif, der Standard Garbage Collector G1 bremst die JVM mitunter merklich aus. Mit Java 15 gelten die beiden potenziellen Nachfolger Z Garbage Collector (JEP 377) und Shenandoah (JEP 379) als produktionsreif. Die Anwender schalten sie über die Optionen »-XX:+UseZGC« beziehungsweise »-XX:+UseShenandoahGC« ein.
Im Experiment sank der Zeitaufwand beim Einsatz des Z Garbage Collectors gegenüber G1 um zirka ein Drittel. Shenandoah arbeitet hingegen permanent im Hintergrund und macht so große Aufräumarbeiten überflüssig. Welcher der beiden Kollektoren sich jetzt besser schlägt, hängt von der Art des Speicherverbrauchs in der Anwendung ab.
Hinaus!
Die schon vor längerer Zeit geäußerte Drohung, den teilweise 20 Jahre alten Quelltext von Java aufzuräumen, setzen einige Entwickler nun offenbar in die Tat um. Grund dafür sind nicht zuletzt Sparmaßnahmen: Was nicht da ist, braucht weder Pflege noch Tests.
Prominentestes Opfer ist sicherlich der Javascript-Interpreter Nashorn. Die mit Java 8 eingeführte Software galt bereits mit Java 11 als hoffnungslos veraltet. Die Entwickler werfen sie nach zwei Jahren im Status »deprecated« nun komplett über Bord (JEP 372). Wer weiterhin Javascript in seinen Java-Programmen einsetzen möchte, der greift entweder auf die GraalVM [6] oder auf den älteren Rhino-Interpreter [7] zurück.
Für die RMI Activation gibt es hingegen keine Alternative, Java 15 streicht sie ersatzlos. Das obskure Feature wurde bereits in Java 8 als optional markiert und wandert nun mit JEP 385 ins Nirvana.
Nach nur sechs Monaten im Zustand »deprecated« gibt es künftig auch kein Java mehr für Solaris (JEP 381). Die wenigen verbliebenen Solaris-Kunden scheinen Oracle nicht mehr den Aufwand wert zu sein, weiterhin den Port für das eigene, proprietäre Unix zu pflegen.
Einen von außen erzwungenen Rauswurf erlebt hingegen OpenGL im Apple-Universum. Sowohl das JDK als auch JavaFX nutzten OpenGL bislang zum Rendern von Oberflächen. Beide setzen inzwischen zwangsweise auf Apples proprietäre Metal-Schnittstelle (JEP 382).
Beim DatagramSocket gingen die Entwickler hingegen weniger radikal vor. Sie entschieden sich für ein eher vorsichtiges Aufräumen und gegen die Abrissbirne. Der DatagramSocket dient zum Versenden und Empfangen von UDP-Paketen, wichtige Protokolle wie DNS, NFS oder UPnP basieren darauf.
Die Implementierung von Java, bisher eine Mischung aus C- und Java-Code, soll einer moderneren Variante mit weniger Pflegeaufwand weichen (JEP 373). Ganz auf den Müll kommt die alte Lösung jedoch nicht: Sie steckt auch weiterhin in Java 15 und lässt sich bei Bedarf über den Parameter »-Djdk.net.usePlainDatagramSocketImpl« aktivieren.
Fazit
Für Java geht es im Zuge des alle sechs Monate erscheinenden Feature-Releases auch diesmal wieder ein gutes Stück vorwärts. Neben der vorsichtigen Erweiterung der Syntax selbst (Textblöcke, Pattern Matching) gibt es mit den Records ein komplett neues Feature. Die Summe der seit Java 11 freigegebenen Funktionen macht Java 15 erstmals attraktiv genug, um es als Alternative zu Java 11 für die tägliche Arbeit einzusetzen.
Für das verbleibende Jahr steht neben Arbeiten an leichtgewichtigen Threads (Project Loom [8]) und dem Anpassen an aktuelle Prozessorarchitekturen (Project Valhalla [9]) noch ein Kulturwandel ganz anderer Art bevor. Das Skara-Projekt will den Java-Quellcode noch in diesem Jahr von Suns eigener Quellcodeverwaltung Mercurial auf Git umziehen. Java würde dann auf Github landen – vor wenigen Jahren noch undenkbar.
In dieser Beziehung schon weiter sind die Partnerprojekte GraalVM und JavaFX, die bereits beide auf Github warten. Die GraalVM6 als alternative JVM ermöglicht auf der einen Seite den Einsatz anderer Sprachen, auf der anderen Seite komplett native Anwendungen auf Basis von Java-Code. Die aktuelle Graal-Version 20.2 unterstützt bereits Features aus ECMAScript 2021, was den Rauswurf des Nashorn-Interpreters (unterstützt ECMAScript-262 5.1 von 2011) verschmerzen lässt. Voll auf native Images setzt Quarkus [10]: Serveranwendungen sollen damit in einem Bruchteil der Zeit starten und dabei auch noch weniger Speicher benötigen.
Die Vorzüge färben auch auf den Desktop ab. Gluon stellt hierfür Substrate [11] vor, ein Werkzeug, um JavaFX-Anwendungen in native Images zu kompilieren. Zeitnah zu Java 15 wird auch JavaFX 15 erwartet, das aktuelle Basisbibliotheken unterstützen soll und Bugfixes erhält. (kki)
Der Autor
Carsten Zerbst erstellt mit seinem Team Individualsoftware für die Automobil-, Luft- und Raumfahrtindustrie sowie für den Schiffbau. Er sucht weitere Mitarbeitende für abwechslungsreiche Tätigkeiten mitten in Hamburg.
Infos
- Adopt OpenJDK: https://adoptopenjdk.net
- Amber: https://openjdk.java.net/projects/amber
- Express: https://en.wikipedia.org/wiki/EXPRESS_(data_modeling_language)
- Memcached: https://memcached.org
- Apache Ignite: https://ignite.apache.org
- GraalVM: https://www.graalvm.org
- Mozilla Rhino: https://developer.mozilla.org/de/docs/Rhino
- Loom: https://openjdk.java.net/projects/loom/
- Valhalla: https://openjdk.java.net/projects/valhalla/
- Quarkus.io: https://quarkus.io
- Substrate: https://github.com/gluonhq/substrate






