Ein Ausblick auf Java 8

20. Februar 2014Eric Giese

Am 18. März ist es soweit: Java geht in die achte Runde. Die erste vollständig unter der Schirmherrschaft von Oracle entwickelte Version bringt lang erwartete Veränderungen. Zwei davon erregen ein besonderes Interesse:

  • Mit der Einführung von Lambda Expressions ist Java nach C# und C++ eine der letzten objektorientierten Mainstream-Sprachen, die funktionale Konstrukte in ihren Sprachkern aufnimmt
  • Interfaces erlauben jetzt die Deklaration von statischen Methoden und Methodenimplementierungen

Spannend ist an dieser Stelle vor allem, welche Auswirkungen diese Sprachänderungen auf das Anwendungsdesign haben: Was wird einfacher? Was kann zukünftig effizienter gelöst werden als bisher?

Lambda Expressions

Lambda Expressions (kurz Lambdas) erlauben es, mit minimalen Aufwand Interfaces zu implementieren, die genau eine abstrakte Methode haben:

 

Bisher sah dieser Code, mit den sogenannten „inneren Klassen“, noch so aus:

 

Auf den ersten Blick sieht dies nur nach einer kürzeren Schreibweise für innere Klassen aus, deren Syntax sich stark an Scala und C# anlehnt. Bei der Umsetzung handelt es sich allerdings um weitaus mehr, wie Brian Goetz, der Chefdesigner des Features, erklärt.

Die Einführung dieser harmlos aussehenden Verbesserung hat eine lange, leidvolle Geschichte. Lambdas sollten bereits mit Java 7 eingeführt werden – es gab bereits 2008 drei verschiedene Prototypen, über die diskutiert wurde. Aufgrund der Uneinigkeit über die Umsetzung und den finanziellen Schwierigkeiten von Sun wurde das Feature jedoch in die Warteschleife gesetzt.

Erst die Übernahme durch Oracle sollte das ändern: Die positiven Erfahrungen mit Lambdas in alternativen JVM-Sprachen wie Groovy und Scala und vor allem die zunehmende Bedeutung der parallelen Datenverarbeitung haben nach etlichen Verzögerungen dazu geführt, dass das Feature mit Java 8 endlich ausgeliefert wird.

Anwendungen, die viele innere Klassen einsetzen, können nun in großem Umfang Code sparen. Das betrifft vor allem grafische Oberflächen und das Multithreading: Mit inneren Klassen werden hier verzögerte Verarbeitungsschritte und asynchrone Reaktionen beschrieben. Während Java-GUI-Anwendungen weiterhin keine große Bedeutung haben, ist das Potenzial beim Multithreading durch die immer weiter zunehmende Anzahl an CPU-Kernen enorm: Die bisher in Java üblichen imperativen Algorithmen funktionieren nur sequentiell, nicht aber parallel. Sie sind damit für eine Verteilung auf mehrere Prozesse ungeeignet.

Parallele Datenverarbeitung mit Streams

Lambdas machen das Schreiben von parallelisierbaren Algorithmen einfacher, aber noch nicht einfach genug. Deshalb wurde speziell für diesen Zweck eine neue API eingeführt: java.util.stream. Mit ihr lassen sich beliebige Datenmengen sequentiell oder parallel filtern, transformieren, sortieren und zusammenfassen. Sie ist damit das Herzstück der Neuerungen im JDK und wird für die meisten Entwickler auch den Erstkontakt mit den neuen Sprachfunktionen darstellen.

Ein Beispiel:

Diese Operation ermittelt parallel aus einer Menge von Kunden die sortierten Namen (inkl. Umlaute) aller über 18-jährigen Kunden als Liste, bereinigt um alle Duplikate. Eine ähnliche Implementierung, wäre mit den Bordmitteln von Java 7 aufwändiger zu schreiben, fehleranfälliger und oft auch ineffizienter in der Ausführung.1

Die Stärke der neuen API: Sie ist deklarativ, es wird nur beschrieben was zu tun ist, nicht jedoch wie. Die Stream-Implementierung erhält dadurch zahlreiche Freiheiten, die eine sehr effiziente Verarbeitung möglich machen, wie zum Beispiel die freie Wahl einer Parallelisierungsstrategie der Iteration und des Sortieralgorithmus.

Die Verwendung der Stream-API sollte insbesondere denjenigen Entwicklern leicht fallen, die bereits Erfahrung mit SQL haben. SQL-Abfragen sind rein deklarativ, die Durchführung der Operation wird vollkommen dem Datenbanksystem überlassen.

Ebenfalls eine große Ähnlichkeit besteht zu .NET‘s LINQ. Der Unterschied zwischen den beiden Libraries liegt eher in der Benennung der Methoden als in der Umsetzung. LINQ verwendet SQL-Begriffe, Java orientiert sich an funktionalen Sprachen: select vs. map, where vs. filter etc. Welches Framework nun „besser“ ist, sei dahingestellt und spielt in der Praxis auch keine Rolle. Viel wichtiger ist, dass Java-Entwickler nach langer Abstinenz nun endlich über Möglichkeiten verfügen, die C#-Entwickler schon seit Jahren haben.

Methodenimplementierungen in Interfaces

Java Interfaces dürfen zukünftig selbst Methoden implementieren. Ein Beispiel dafür ist die oben aufgerufene neue Methode stream () am Interface Collection:

 

Dadurch wird Vererbung in Java wieder attraktiver. Die bisherigen Mechanismen mit abstrakten Klassen haben nur einen einzigen Pfad erlaubt, um Implementierungen zu erben. Das war sehr ungünstig und hat oft zu „tiefen“ und komplexen Objekthierarchien geführt. Durch Interfaces mit Default-Methoden hingegen wird es möglich, „flache“, aber sehr breite Hierarchien zu schaffen, in denen Funktionen nur bei Bedarf „beigemischt“ werden.

Die Extremform dieser Auslegung findet in Scala statt, deren mit Interfaces vergleichbare Traits sogar Felder deklarieren dürfen. In Scala übernimmt die Vererbung Aufgaben, die man in Java eher über eine Komposition löst. Hier wird zum Beispiel ein Range von n…m definiert als eine Sequenz (~Liste), die indiziert, parallelisierbar und serialisierbar ist:

 

Java steht nun ähnliches offen, wenn auch in abgeschwächter Form. Anders als Lambdas sind die Default-Methoden ein Feature, dessen Nutzen nicht sofort offensichtlich ist. Im Alltag wird man häufig unbewusst die neuen Default-Methoden wie stream() nutzen, aber nur selten selbst welche definieren. Für Library-Designer ist das Feature natürlich ungleich wichtiger: Es ist aber davon auszugehen, dass es hier, ähnlich wie bei den mit Java 5 eingeführten Generics, noch einige Zeit dauern wird, bis sich Best Practices für den sinnvollen Einsatz etabliert haben.

Fazit

Lambda Expressions und die verbesserten Interfaces werden den Entwurf von Bibliotheken und Applikationen beeinflussen. Das wird insbesondere für diejenigen relevant, die fundamentale Designentscheidungen treffen: Mit der Evolution von Sprachen ändern sich die Paradigmen und die Art, wie API’s am besten entworfen werden. C++-Entwickler können davon ein Lied singen…

Das Ausnutzen mehrerer CPU-Kerne zur Beschleunigung langsamer Operationen war bisher sehr mühsam, das ändert sich nun dramatisch. Durch die Stream-API und Lambdas wird der dafür nötige Code viel übersichtlicher und kürzer. Gut parallelisierbare Anwendungsbereiche sollten deshalb einen frühzeitigen Wechsel auf Java 8 erwägen.

Auch wenn Java 8 erst Mitte März herauskommt und die Zyklen bis zum Einsatz einer neuen JVM-Version in den meisten Unternehmen sehr lang sind: Es gibt neben den hier genannten noch viele weitere Neuerungen, die es wert sind, sich mit ihnen frühzeitig zu befassen. Ich hoffe deshalb, dass die Adaptionszeit für die neue Java-Version geringer sein wird als dies bei bisherigen Versionen der Fall war.

Java braucht den Wechsel, um endlich wieder auf der Höhe der Zeit zu sein. Sehen Sie das auch so? Haben Sie sich schon mit Java 8 beschäftigt? Ich bin gespannt auf Ihre Kommentare.

Tags: , ,
Eric Giese Eric Giese ist Softwareentwickler bei adesso. Seine Schwerpunkte liegen auf den Themen Java und Testen.
Artikel bewerten:
1 Star2 Stars3 Stars4 Stars5 Stars
Loading...

Kommentar hinzufügen:

Ihr Kommentar: