Java ist nach wie vor eine der weit verbreitetsten Programmiersprachen weltweit, und genießt weiterhin eine große Beliebtheit. Einer der Gründe ist die stetige Veränderung und Verbesserung der Sprache, und damit wollen wir uns in diesem Beitrag beschäftigen. Der Fokus liegt auf den Sprachfeatures und damit weder auf die Performance-Verbesserungen, noch auf externen Tools.
Release Intervall
Seit der Geburt von Java 1.0 im Jahre 1996 haben sich die Release-Strategien mehrfach geändert. Waren die Releases anfangs noch jährlich, so wurden die Schritte im Laufe der Zeit immer größer. Entwickler:innen mussten somit immer länger auf neue Features warten und somit auch vieles auf einen Schlag neu lernen. Dies änderte sich dann ab Version 9. Ab Java 9 werden neue Releases halbjährlich veröffentlicht und das hält bis zum heutigen Tag an. In jeder Iteration gibt es manchmal interne Veränderungen oder aber auch neue Features, die den Programmieralltag verbessern sollen.
Die Änderung des Intervalls zieht viele Vorteile nach sich. Entwickler:innen werden nicht an der Flut der Neuerungen überrannt. Anpassungen am Code können leichter vorgenommen werden. Die Lernkurve ist deutlich flacher und Drittanbieter können schneller und einfacher sich an die Änderungen anpassen.
Java Features Roadmap
In diesem Beitrag konzentrieren wir uns auf Features, die ab Java 8 und weiter hinzugekommen sind. Da ich Java selbst erst ab der Version 7 kenne, war für mich ab 8 alles Neuland. Zugegeben war ich anfangs selbst kein Freund der neuen Features, die mit Java 8 einher gekommen sind. Kaum hatte ich mich mit Java 7 während meines Studiums zurechtgefunden, kam schon Java 8 um die Ecke, vollgeladen mit jeder Menge neuer Features.
Welche Java Features gibt es denn seit Java 8? Welche sind mit 9 dazugekommen, 10, 11, 12, usw.? Eine kleine Tabelle soll uns helfen, die Features bisschen besser einordnen zu können.
Version | Funktionen |
---|---|
Java 1.0 | Grundfunktionen: automatische Speicherbereinigung, Ausnahmen, Threads, Java-API (Strings, Vektoren, Hashmaps usw.) |
Java 1.1 | JDBC, innere Klassen, Java Beans, Reflection, RMI |
Java 1.2 | Swing-API, Collections Framework, Java-Speichermodell |
Java 1.3 | HotSpot JVM, JavaSound, JNDI |
Java 1.4 | Schlüsselwort ‚assert‘, reguläre Ausdrücke, Exception Chaining, NIO-Paket |
Java 1.5 | Generics, Annotationen, Autoboxing/Unboxing, Enums, Varargs, For-Each-Schleife |
Java 1.6 | @Override-Annotation, integrierte Webdienstunterstützung |
Java 1.7 | Try-with-Resources, Diamant-Operator, Unterstützung für dynamische Sprachen |
Java 1.8 | Lambda-Ausdrücke, Streams-API, Optionals-API, funktionale Schnittstellen, neue Date-Time-API |
Java 9 | Modulsystem (Project Jigsaw), jshell, verbessertes Javadoc |
Java 10 | Typinferenz für lokale Variablen (var), kleinere Funktionserweiterungen |
Java 11 | Entfernung alter Technologien (Java EE-Module, CORBA), HttpClient-API |
Java 12 | Switch-Ausdrücke, Textblöcke, Records |
Java 13 | Textblöcke, dynamische CDS-Archive |
Java 14 | Pattern Matching für instanceof, Records (Preview), hilfreiche NullPointerExceptions |
Java 15 | Sealed Classes (Preview), Hidden Classes, Entfernung des Nashorn JavaScript Engines |
Java 16 | Records (endgültig), Pattern Matching für instanceof (endgültig), Sealed Classes (zweite Preview) |
Java 17 | Sealed Classes (endgültig), Pattern Matching für switch (Preview) |
Java 18 | Java Web Server, Code Snippets in der Java API Dokumentation, Pattern Matching für switch (Preview) |
Java 19 | Pattern Matching für switch (Preview), Record Patterns (Preview), Virtual Threads |
Java 20 | Record Patterns (Preview), Scoped Values (Incubator) |
Vorstellung der Features
Die Änderungen sind beachtlich. Wenn man nicht jedes dieser Features kennt, ist das nicht schlimm, ich kenne auch nicht alle. Der Fokus soll hier auf den Features liegen, mit den größten Auswirkungen für die Entwickler:innen, so zumindest mein persönlicher Eindruck. Dazu habe ich die in meinen Augen wichtigsten Versionen grün markiert und die wichtigsten Features orange, siehe Tabelle 1.
Functions mit Streams und Optionals
Mit Java 8 kamen mit der Einführung des funktionalen Paradigmas eine der größten Änderungen für Entwickler:innen. Das funktionale Paradigma führte Predicates, Consumer, Supplier, Functions und viele weitere Funktionsbausteine ein. All diese Neurungen kann man auch als Lambda Ausdruck schreiben. In der Streams API und Optionals API, welche auch neu mit dem Release sind, werden die Funktionsbausteine/Lambdas anschließend verwendet.
Was sind das für Neuerungen und was machen die denn genau? Im Detail diese durchzusprechen würde den Rahmen des Beitrags sprengen, daher hier eine kurze Zusammenfassung:
Predicate<T>
- Nimmt ein beliebiges Objekt
T
entgegen und überprüft dessen Eigenschaften und liefert anschließend einen booleschen Ausdruck (true
oderfalse
) zurück.
- Nimmt ein beliebiges Objekt
final Predicate<Book> isComedyGenre = book -> book.genre().contains("comedy");
Consumer<T>
- Nimmt ein beliebiges Objekt
T
entgegen und verarbeitet dieses und liefert anschließend nichts (void
) zurück.
- Nimmt ein beliebiges Objekt
final Consumer<List<String>> plotBookGenre = genres -> System.out.println(genres);
Supplier<T>
- Nimmt kein Objekt entgegen, stellt jedoch ein beliebiges Objekt
T
bereit.
- Nimmt kein Objekt entgegen, stellt jedoch ein beliebiges Objekt
final Supplier<List<Book>> allBooks = () -> db.findAll();
Function<T,R>
- Nimmt ein beliebiges Objekt
T
entgegen und macht diverse Operationen und liefert anschließend ein ObjektR
zurück, was theoretisch vom selben Typ wieT
sein darf.
- Nimmt ein beliebiges Objekt
final Function<Book, List<String>> bookGenre = book -> book.genre();
Stream<T>
- Verarbeitet sequenzielle und parallele Datenstrukturen von Objekten
T
mittels funktionaler Programmierung. WandeltT
evtl. in andere Typen um und packt diese in eineCollection<T>
,Map<K,V>
oder überführt in die Optionals API mit einem einzelnen Element.
- Verarbeitet sequenzielle und parallele Datenstrukturen von Objekten
allBooks.get()
.stream()
.filter(isComedyGenre)
.map(bookGenre)
.forEach(plotBookGenre);
Optional<T>
- Stellt eine Containerklasse bereit, die den Umgang mit potenziell fehlenden Werten verbessert und die Anzahl von Nullwert-Fehlern reduziert.
final Optional<Book> bookOfComedy = allBooks.get()
.stream().filter(isComedyGenre)
.findFirst();
Lambda Schreibweise
Eine weitere Verbesserung ist die Verwendung von Lambdas auch für anonyme Implementierungen wie z. B. den ActionListener
bei der Erstellung von grafischen Oberflächen mit Swing. Das ist nur ein Beispiel von vielen, wo sich durch die Schreibweise viele unnötige Zeilen von Code sparen lassen.
final var button = new JButton("button");
// old anonymous implementation way
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("clicked: " + e.getActionCommand());
}
});
// new lambda way
button.addActionListener(e -> System.out.println("clicked: " + e.getActionCommand()));
Typ-Herleitung via var
Mit Java 10 kam eine sehr kleine Änderung hinzu, doch mit sehr großen Auswirkungen, die Rede ist von var
. Die meisten kennen dieses Feature schon aus TypeScript, C# oder Kotlin und es war nur eine Frage der Zeit bis dieses Schlüsselwort endlich bei Java eingeführt wird. Damit lässt sich nun endlich viel einfacher eine Variable innerhalb einer Methode zuweisen. var
birgt natürlich auch Risiken, da der Typ nicht sofort ersichtlich ist, oder der Variablenname nicht gut gewählt ist oder Kontext es nicht preisgibt.
final var bookById = new HashMap<Long, Book>();
Switch Expressions und Textblöcke
Mit den Switch Expressions kommt eine weitere Änderung. Dadurch werden die Ausdrücke kompakter, man kann Switch Expressions nun auch direkt zurückgeben und ein neues Schlüsselwort yield
ersetzt das vorherige break
, wenn man mit Lambdas arbeitet. Hier ein Beispiel, wie es nun aussehen kann.
static Season determineSeason(final Month month) {
return switch (month) {
case JANUARY, FEBRUARY, MARCH -> Season.SPRING;
case APRIL, MAY, JUNE -> Season.SUMMER;
case JULY, AUGUST, SEPTEMBER -> Season.FALL;
case OCTOBER, NOVEMBER, DECEMBER -> Season.WINTER;
};
}
enum Season {
SPRING, SUMMER, FALL, WINTER
}
Records und Sealed Classes
Records sind eine weitere tolle Änderung in Java. Dank dieser Datenstruktur ist es nun möglich, mit wenig Aufwand eine unveränderbare Instanz zu erstellen. Außerdem werden Getter, Equals
, HashCode
sowie ToString
automatisch generiert. Erinnert sehr stark an die Drittanbieterbibliothek Lombok, nur ohne die Annotations. Records ist perfekt geeignet, um Domainobjekte oder Austauschobjekte zu definieren. Wenn man jedoch mit Annotations für die JSON Serialisierung oder mit JPA für die Datenspeicherung arbeiten muss, kommt man mit Records schnell an die Grenzen, denn Annotations kann man dort nicht so einfach überall setzen, schade.
record Book(long id, String title, List<String> genre) {
}
Sealed Classes erinnern einen bisschen an die Modularisierung mit Java 9 (Jigsaw). Mit diesem neuen Feature wird beschrieben, wer welche Klasse implementieren darf. Leider geht das nicht in Kombination mit Records, sodass wir wieder auf normale Klassen zurückgreifen müssen.
public sealed class Book permits Textbook, Novel, Encyclopedia {
}
Das Gegenstück z. B. Novel muss eine finale oder eine non-sealed Klasse sein. Es kommt darauf an ob man weitere Vererbung verbieten möchte oder nicht, es muss aber zwingend eins dieser zwei Schlüsselwörter verwendet werden, um Missverständnisse zu vermeiden. Der Einsatz von sealed ist natürlich auch möglich, aber dann benötigen wir wieder das Schlüsselwort permits, denn eine mit sealed versehene Klasse muss immer eine Unterklasse haben.
public sealed class Novel extends Book {
}
Motivation bei der Wahl der Java Version
Nun hat Java diese vielen tollen neuen Features, aber werden diese auch verstanden, akzeptiert und auch verwendet? Dazu gibt es einige Umfragen, die das Ganze uns etwas klarer machen. Zum einen hat Jetbrains seine jährliche Umfrage zu der Werkzeugwahl im Java Ecosystem. Dort sieht man, dass 2021 sowie 2022 Java 8 immer noch sehr stark in Benutzung ist, wobei der Trend zu den höheren LTS Versionen geht. [1] D. h. die neusten Features könnten bekannt sein, aber nicht in Verwendung, da man noch bei Java 8 feststeckt. Ähnliches zeigt der Bericht von New Relic, hierbei ist Java 8 immer noch stark vertreten. [2] Auch Snyk bestätigt ähnliche Daten von 2021. [3]
Woran liegt es nun, dass so viele noch die älteren Versionen benutzen? Das kann natürlich vielerlei Gründe haben. Ein Grund, warum man bei den alten Versionen bleibt, sind die Sicherheitslücken. Es gibt das Sprichwort „Altbewährtes währt am längsten“ und hier ist das auch ein valides Argument. Die ganzen Kinderkrankheiten frisch nach dem Release werden eliminiert und härtet über die Jahre aus. So hat Java 8 die wenigsten Sicherheitslücken im JRE. Einen weiteren Grund können firmeninterne Richtlinien liefern, die dazu Projekte zwingen, auf einer bestimmten Version zu bleiben, z. B. weil alle Projekte dieselbe Version bestimmter Software haben sollten, um die Kompatibilität zu gewährleisten. Ein anderes Argument könnte der Aufwand und die damit verbundenen Kosten sein. Auch wenn Java mit der Abwärtskompatibilität wirbt, ist ein Upgrade der Major Version leider doch nicht so ganz reibungslos, vor allem können schnell Abhängigkeiten von Drittanbietern dazwischenfunken und die Migration erschweren. Eine weitere Begründung könnte einfach das fehlende Wissen sein, dass es überhaupt Updates gibt oder neue Features gibt, wenn man nicht aktiv danach sucht, um auf dem Laufenden zu bleiben, kann man so recht schnell den Anschluss verlieren. Der letzte Punkt, der mir dazu einfällt, könnte einfach die Komfortzone sein, der ich auch eine Zeit lang verfallen war. Neues zu lernen, kann oft doch anstrengend sein.
Java Features Umfrage Auswertung
Leider sind die präsentierten Umfragen leider nicht sehr aussagefähig, ob nun Java Features bekannt sind oder sogar benutzt werden. Deshalb war ich so frei und habe eine kleine Umfrage im Unternehmen sowie im Freundeskreis gestartet, um zu sehen, wie dort die Verteilung der Java Versionen ist, welche Features genutzt werden und wie erfahren die Entwickler:innen sind. [5]
Zum einen muss man auf jeden Fall sagen, dass man die neuen Features jetzt nicht überall anwenden muss. Es soll natürlich nur dort Anwendung finden, wo es sinnvoll ist und wirklich etwas verbessert. Es bringt sehr wenig, wenn der Code dadurch unleserlich wird, mehr Komplexität generiert und am Ende die Softwareentwickler:innen sogar langfristig abschreckt.
Clean Code soll weiterhin im Vordergrund stehen und das Abliefern einer qualitativ hochwertigen und sicheren Software, die auch Entwickler:innen und Anwender:innen verstehen und langfristig nutzen.
In der Abbildung 1 ist zu erkennen, dass die LTS Versionen gut vertreten sind. Außerdem erkennt man, dass neuere Versionen persönlich früher Anwendung finden als bei der Arbeit. Zusätzlich sieht man, dass Java 17 stark vertreten ist, persönlich wie professionell. Mit dem kommendem Release im Herbst 2023, erscheint die nächste LTS Version (21) von Java. Laut der Umfrage benutzen mehr als die Hälfte der befragten Personen schon die aktuelle LTS. Die Kluft zwischen der aktuellen und zukünftigen LTS ist also gar nicht so groß, wie anfangs von mir angenommen.
Auch bei der Wissensverteilung und Anwendung der Features erlag ich einem Irrtum. In meinen bisherigen Projekten wurden z.B. Optionals sehr stiefmütterlich behandelt oder wurden missverstanden. Operationen mit Lambdas oder Streams waren schon häufiger. Desto mehr überrascht mich die Auswertung der Umfrage sehr positiv. Die Mehrheit der Befragten kennt die Features und die Meisten nutzen diese sogar bei der Arbeit oder privat. Die Verteilung kann man in Abbildung 2 einsehen.
Fast ¾ der Befragten gaben an, mehr als 6 Jahre Berufserfahrung zu haben, siehe Abbildung 3. Eine große Mehrheit der erfahrenen Entwickler:innen nutzt auch Java 11 oder höher. Sodass man annehmen kann ,dass die Features schon lange nicht mehr unbekannt sind und Anwendung finden.
Zusammenfassung
Als begeisterter Java-Entwickler bin ich von der Breite und Tiefe der Funktionen beeindruckt, die Java bietet. Nach der Recherche war ich positiv überrascht, wie viele von uns die angebotenen Features tatsächlich kennen und nutzen. Es ist ein Beweis dafür, dass wir als Gemeinschaft nicht nur lernen und wachsen, sondern auch aktiv die Entwicklungen unserer Lieblingssprache mitgestalten.
Es hat mich besonders gefreut zu sehen, wie viele von uns bereits Java 17 verwenden, die aktuellste LTS-Version. Diese Anpassungsfähigkeit und Bereitschaft, die neuesten Versionen zu übernehmen, ist ein weiterer Beweis für die Lebendigkeit und Vitalität der Community. Es zeigt auch unser Engagement für kontinuierliches Lernen und Verbessern, zwei Kernaspekte der Softwareentwicklung.
Besonders hervorheben möchte ich die Verwendung von Optionals in Java. Diese Funktion ist ein unschätzbares Werkzeug in unserem Programmierwerkzeugkasten. Es hilft uns, die traditionellen null-Referenz-Probleme, die in der Vergangenheit zu so vielen Fehlern geführt haben, zu vermeiden. Die Optionals in Java bieten uns eine robuste und elegante Möglichkeit, diese Probleme zu lösen und dabei den Code lesbar und verständlich zu halten.
Ich bin stolz auf das, was wir als Java-Gemeinschaft erreicht haben, und freue mich auf alles, was noch kommt. Mit den ständig weiterentwickelten Funktionen und unserer unermüdlichen Neugierde wird Java sicherlich weiterhin eine wichtige Rolle in unserer Arbeit und auch privat spielen. Gemeinsam können wir sicherstellen, dass Java weiterhin eine der zuverlässigsten, leistungsfähigsten und angesehensten Sprachen in der Welt der Softwareentwicklung bleibt. Falls jemand noch Lust hat an der Umfrage teilzunehmen, hier geht es zur Umfrage.
Nachweise
[1] Java Programming – The State of Developer Ecosystem in 2022 Infographic | JetBrains: Developer Tools for Professionals and Teams
[2] 2022 State of the Java Ecosystem Report | New Relic
[3] JVM Ecosystem Report 2021 | Snyk
[4] Versions of Oracle JRE : Versions and number of related security vulnerabilities (cvedetails.com)
[5] Java Features Umfrage