Fallstricke in Java 8

Dienstag, 3.1.2017

Seit 2014 gibt es in Java die Möglichkeit, funktional zu programmieren. Die mit Java 8 eingeführten Lambda-Ausdrücke sind ein mächtiges Feature, das aber einige Fallstricke mit sich bringt. Eine kleine Sammlung habe ich bereits in einem Fachbeitrag für das JavaSpektrum 6/2014 zusammengestellt. Die Sammlung beschränkt sich nicht nur auf Lambda-Ausdrücke und Streams, sondern auch auf andere Neuerungen wie die Date-/Time-API. 

Da mittlerweile viele unserer Kunden den Umstieg auf Java 8 durchgeführt haben und auch erste Erfahrungen mit den neuen Möglichkeiten sammeln, habe ich die Sammlung um zwei Tipps erweitert.

Optional als reduce-Ergebnis vermeiden

Mit der reduce-Funktion lassen sich Summen oder andere Aggregatfunktionen über alle Elemente eines Streams berechnen. Ein einfaches Beispiel für die Summenbildung ist in dem folgenden Code-Schnipsel gezeigt.

Stream<Integer> values = IntStream.range(0, 5).boxed();
Optional<Integer> sum = values.reduce(Integer::sum);

Über boxed() wird aus dem primitiven IntStream in einen Stream<Integer> umgewandelt. Dann wird die Summe mit der Stream-Methode reduce und mit der statischen sum-Methode der Klasse Integer gebildet. Als Ergebnis bekommt man ein optionales Integer zurück. Warum ein Optional? Nun ja, wenn der Stream keine Elemente enthält, kann reduce nicht angewendet werden und das Ergebnis undefiniert. Und seit Java 8 ist es gute Praxis, in dem Fall ein Optional statt null zurückzugeben.

In den allermeisten Fällen wird man für einen leeren Stream die Summe Null als numerischen Wert annehmen. Statt der Fallunterscheidung mit Prüfung, ob sum.isPresent()==false, um dann mit sum=0 weiterzuarbeiten, gibt es eine einfachere Lösung:

Integer sum = values.reduce(0, Integer::sum);

Wenn man als Startwert der reduce-Funktion die numerische 0 vorgibt, ist das Ergebnis für einen leeren Stream ebenfalls 0 und der Rückgabetyp Integer und kein Optional. Die Fallunterscheidungen anhand von Optional kann man sich damit auf einfache Weise sparen.

Sammeln von Objekten in einer Map

Ich habe des Öfteren Code geschrieben und gesehen, der Objekte in einer Map sammelt anhand eines oder auch unterschiedlicher Schlüsselattribute des Objekts. Nehmen wir als Beispiel eine Klasse Person, die mindestens die beiden Attribute steuerId und name hat. Ausgehend von einer Liste von Personen besteht die Aufgabe darin, eine Map mit der SteuerId als Schlüssel zu erzeugen. Vor der Einführung von Lambda-Ausdrücken war das recht umständlich: Map erzeugen, For-Schleife über die Liste, Schlüssel und Wert aus den Objektattributen ermitteln und jeden Eintrag in die Map einfügen. Mit Java 8 gibt es die collect()-Methode auf Streams und die Klasse Collectors, die eine Reihe von nützlichen Methoden zum Sammeln in Collections, Summieren oder Gruppieren der Elemente eines Streams bereitstellt. Damit ist die Aufgabe ein Einzeiler, wenn man die Methoden-Referenzen der Attribute benutzt:

Map<String, String> idZuName = personen.stream().collect(
Collectors.toMap(Person::getSteuerId, Person::getName));

Doch jetzt haben wir als Wert nur das eine Attribut name. Was tun, wenn man das ganze Person-Objekt als Wert in der Map haben möchte? Die Lösung ist erstaunlich einfach, aber nicht direkt offensichtlich:

Map<String, Person> idZuPerson = personen.stream().collect(
Collectors.toMap(Person::getSteuerId, Function.identity()));

Über die generische Identitätsfunktion Function.identity() bekommt man angewendet auf das Person-Objekt im Stream genau dieses zurück und kann es als Wert in die Map schreiben. Es kann so einfach sein. Ich habe leider länger danach gesucht und als ich es gefunden habe, wie so häufig dieses zweischneidige Aha-Erlebnis gehabt: Freude über die Einfachheit der Lösung gepaart mit dem Ärger über sich selbst, nicht früher darauf gekommen zu sein.

 

kostenloser Download

 


zurück zur Blogübersicht

Diese Beiträge könnten Sie ebenfalls interessieren

Keinen Beitrag verpassen – viadee Blog abonnieren

Jetzt Blog abonnieren!

Kommentare

Tobias Voß

Tobias Voß

Tobias Voß arbeitet als IT-Architekt in agilen Projekten bei der viadee. Er berät Kunden im Versicherungs- und Bankenumfeld bei der Umsetzung individueller Softwaresysteme mit den Schwerpunkten Java, Architektur und Prozessautomatisierung und leitet den Kompetenzbereich Java & Architektur der viadee.

Tobias Voß bei Xing  Tobias Voß auf Twitter