Lambda Ausdrücke und Default-Methoden in Java SE 8

Lambda Ausdrücke und Default-Methoden in Java SE 8

Mit der Version 8 der Java Standard Edition stehen Java-Entwicklern ab sofort zwei mächtige neue Sprach-Features zur Verfügung: Lambda Ausdrücke (“Lambda Expressions”, inoffiziell auch “Closures” genannt), welche in anderen JVM-Sprachen wie Groovy, Scala und insb. Clojure bereits vorher unterstützt wurden, sowie Default-Methoden (“Virtual Extension Methods”) für Interfaces. Beide Konstrukte ermöglichen es, in Java viel eleganter als bisher Elemente der funktionalen Programmierung, wie etwa die Anwendung von Funktionen auf Streams zu nutzen. Der ehemalige Neofoniker und jetzige Senior Softwareentwickler bei idealo, Martin Gerlach, beschreibt die wichtigsten Neuerungen im Gastbeitrag.

Java Lambda Ausdrücke sparen viel Platz

Lambda Ausdrücke beschreiben Funktionsobjekte. So lässt sich eine Funktion, die die Summe von zwei Argumenten liefert, wie folgt im Code ausdrücken:

(i, j) -> i + j

Die Angabe der Parametertypen ist nur dann erforderlich, wenn der Compiler sie nicht selbst aus dem Kontext des Ausdrucks ableiten kann. Diese Fähigkeit der “Type Inference” spielte erstmals mit der Einführung von Generics zur Verbesserung der Typsicherheit und Vermeidung der Notwendigkeit von Typumwandlungen (“Typecasts”) bei Verwendung von Collections in Java SE 5 (Sept. 2004) eine Rolle und wurde mit den folgenden Versionen immer weiter verbessert, zuletzt in Java SE 7 mit dem “Diamond Operator” (<>).

Tatsächlich kann man obigen Ausdruck an jeder Stelle einsetzen, an der eine Variable oder ein Parameter durch die Angabe eines Interfaces mit genau einer abstrakten Methode, eines sog. funktionalen Interfaces typisiert ist, z.B.

interface IntOp { int apply(int arg1, int arg2); }
interface DoubleOp { double apply(double arg1, double arg2); }
…
int calcInt(int i1, int i2, IntOp op) { return op.apply(i1, i2); }
int calcDouble(double d1, double d2, DoubleOp op) {
return op.apply(d1, d2); }
…
int i1 = calcInt(1, 2, (i, j) -> i + j); // i1 == 3
int i2 = calcInt(1, 2, (i, j) -> i – j); // i2 == -1
double d = calcDouble(1.1, 2.2, (i, j) -> i + j); // d == 3.3

Dabei ist es unerheblich wie die abstrakte Interface-Methode heißt, wichtig sind die Typen der formalen Parameter und des Rückgabewertes. Der Compiler weiß anhand der Methodendeklaration, dass die Funktionsargumente (Methodenparameter) i und j in den  Ausdrücken für i1 und i2 vom Typ int sein müssen und im Ausdruck für d vom Typ double. Autoboxing/-unboxing sowie implizite Typenumwandlungen (z.B. von int nach long) funktionieren dabei wie gewohnt.

Lambda-Ausdrücke sparen so nicht nur viel Platz, sondern erhöhen die Lesbarkeit und damit auch die Wartbarkeit gegenüber Code wie diesem hier ungemein:

int i1 = calcInt(1, 2, new IntOperator() { // before Java 8…

    @Override
    public int apply(int i, int j) {
         return i + j;
    }
});

Allgemeine funktionale Interfaces finden sich in Java 8 im Package java.util.function, darunter auch Function<T, R> { R apply (T t); } und diverse Varianten für primitive Typen. Diese Interfaces können überall dort als Parameter-Typen für Methoden verwendet werden, wo aufrufender Code Java Lambda Ausdrücke einsetzen kann beziehungsweise können soll.

Aber auch für Parameter die mit “alten” funktionalen Interfaces wie Comparator<T> (siehe weiter unten), oder auch dem Spring-jdbc RowMapper<T> typisiert sind, können Lamdba Ausdrücke eingesetzt werden, z.B. im folgenden Aufruf von public <T> List<T> JdbcTemplate.query(String, RowMapper<T>):

List<ID> customerIDs = jdbcTemplate.query(„select id from customer“,
(resultSet, rowNum) -> new ID(resultSet.getLong(rowNum)));

Methodenreferenzen

Eine interessante Ausprägung von Java Lambda Ausdrücken sind Methodenreferenzen. Der Ausdruck für i1 aus dem obigen Beispiel lässt sich auch wie folgt schreiben:

int i1 = calcInt(1, 2, Integer::sum);

Das ist äquivalent zu:

int i1 = calcInt(1, 2, (i, j) -> Integer.sum(i, j));

Nicht nur statische Methoden wie Integer.sum(int, int), sondern auch Konstruktoren für Objekte und Arrays sowie Instanzmethoden lassen sich so als Funktionen übergeben, sofern formale Parameter und Rückgabewerte zusammenpassen.

Konstruktoren-Referenzen schreibt man mittels ::new, z.B. ArrayList::newString[]::newint[]::new. Damit kann nicht nur der Default-Konstruktor referenziert werden, sondern auch Konstruktoren mit Parametern. Es kommt darauf an, welcher gerade passt. Wird z.B. die Methode

void myMethod(Function<Integer, List<String>> f) { … f.apply(2); … }

wie folgt aufgerufen: myMethod(ArrayList::new);

so wäre das Äquivalent zu myMethod(n -> new ArrayList<>(n));  und innerhalb der Methode würde durch den Aufruf f.apply(2) tatsächlich new ArrayList<String>(2) aufgerufen werden.

Instanzmethoden der Klasse des ersten formalen Parameters der abstrakten Methode eines funktionalen Interfaces können in einem Lambda Ausdruck für diese Methode ebenso referenziert werden, sofern die restlichen Parameter und der Rückgabewert passen:

String[] names = nameService.getAllNames();
Arrays.sort(names, String::compareToIgnoreCase);

public compareToIgnoreCase(String) ist eine Instanzmethode der Klasse String. public static <T> void sort(T[], Comparator<? super T>) ist eine statische Methode der Klasse java.util.Arrays. Weshalb man die Instanzmethode compareToIgnoreCase als Comparator einsetzen darf, wird deutlich wenn man bedenkt, dass Comparator<T> ein funktionales Interface ist. Es gibt nur eine Methode int compare(T a, T b), was nichts anderes heißt, als dass man überall dort, wo ein Comparator erwartet wird, auch einen Lambda-Ausdruck mit zwei Parametern vom Typ T sowie Rückgabewert vom Typ int einsetzen darf. String::compareToIgnoreCase kann man auch als (a, b) -> a.compareToIgnoreCase(b) schreiben. Parameter und Rückgabewert entsprechen genau der abstrakten Methode des Interfaces Comparator<String> – und übrigens auch jener des Interfaces BiFunction<String, String, Integer>:

Comparator<String> cmp1 = String::compareToIgnoreCase;
assert cmp1.compare(„a“, „A“) == 0; // „a“.equalsIgnoreCase(„A“)
BiFunction<String, String, Integer> cmp2 =
                                                                       String::compareToIgnoreCase;
assert cmp2.apply(„a“, „A“) == 0; // „a“.equalsIgnoreCase(„A“)

Instanzmethoden von konkreten Instanzen lassen sich übrigens auch referenzieren:

final String a = „a“;
Function<String, Integer> cmp3 = a::compareToIgnoreCase;
assert cmp3.apply(„A“) == 0; // a.compareToIgnoreCase(„A“)

Default-Methoden in Java 8

Am Beispiel des Interfaces Comparator<T> lässt sich die zweite signifikante Neuerung in Java 8 anschaulich beschreiben. Es gibt in dem Interface nun nicht mehr nur die eine abstrakte Methode int compare(T, T), sondern mit Java 8 auch jede Menge Default-Methoden und statische Methoden. Statische Methoden in Interfaces entsprechen größtenteils statischen Methoden in Klassen, werden jedoch nicht “vererbt”, sondern müssen immer über ihr definierendes Interface in der Form Interface.staticMethod(…) aufgerufen werden. Default-Methoden sind weitaus interessanter. Sie bieten die Möglichkeit, nachträglich Interface-Methoden hinzuzufügen ohne existierenden Code von Klassen, die ein so erweitertes Interface implementieren, verändern zu müssen. Default-Methoden stehen in allen implementierenden Klassen zur Verfügung, werden also vererbt und können auch in implementierenden Klassen überschrieben werden.

Comparator<T> definiert nun einige statische Factory-Methoden sowie Default-Methoden, die dann auf den durch die Factories erzeugten Instanzen aufgerufen werden können. Mit den folgenden (etwas vereinfacht aufgelisteten) Methoden von Comparator<T> …

static <T,U> Comparator<T> comparing(Function<T,U>, Comparator<U>)
static <T> Comparator<T> nullLast(Comparator<T>)
static <T> Comparator<T> naturalOrder()
static <T> Comparator<T> reverseOrder()
default <U> Comparator<T> thenComparing(Function<T,U>, Comparator<U>)

… kann man unter Zuhilfenahme von Methodenreferenzen und einigen Static-Imports z.B. folgenden Comparator für eine Klasse Person mit den Eigenschaften lastName, firstName und birthdate schreiben:

Comparator<Person> cmp =

nullLast(comparing(Person::getBirthdate, reverseOrder()))
.thenComparing(Person::getLastName, naturalOrder())
.thenComparing(Person::getFirstName, naturalOrder());

Wie eine Personenliste durch diesen Comparator sortiert wird, dürfte der Code selbst erklären.
Eine umfassende Auflistung von Interfaces und Klassen, die in ähnlicher Art und Weise erweitert wurden oder in Java 8 neu hinzugefügt wurden, um die Vorteile von Lambda Ausdrücken zu nutzen, findet sich hier.

Da Java-Klassen mehr als ein Interface implementieren können, ist mit Java 8 nun die Mehrfachvererbung von Default-Methoden möglich. Das kann genau wie in anderen Sprachen mit Mehrfachvererbung zu Konflikten führen: Haben zwei Interfaces A und B je eine Default-Methode mit derselben Signatur, z.B. void defaultMethod(), so muss in einer Klasse, die beide Interfaces (direkt oder indirekt) implementiert und in deren Klassenhierarchie(n) die Default-Methode nicht überschrieben wurde, die Default-Methode zwingend überschrieben werden, z.B. in dem explizit eine der beiden Varianten aufgerufen wird:

class X implements A, B {
     @Override public void defaultMethod() {
           A.super.defaultMethod(); // calls implementation in A
     }
}

Alternativ kann die Default-Methode in der Klasse X auch als abstrakt deklariert werden (sofern X auch abstrakt ist), so dass die Konfliktauflösung den Subklassen von X überlassen wird.

Ausführliche Informationen über alle Aspekte von Lambdas und Default-Methoden finden sich beispielsweise in Richard Warburton, “Java 8 Lambdas – Functional Programming for the Masses”, O’Reilly 2014.

Veröffentlicht am 17. November 2014, aktualisiert am 07. Oktober 2020

Teilen auf
Martin Gerlach

Martin Gerlach

Martin Gerlach arbeitet seit 2013 als Senior Softwareentwickler im Bereich Angebotsimport bei idealo, Deutschlands größtem Preisvergleichsportal. Dort befasst er sich mit der Entwicklung perfomanter, skalierbarer Datenimport-, Transformations- und Analysetools. Sein besonderes Interesse gilt dabei verteilten Frameworks für Streaming und Analyse großer Datenmengen sowie damit einhergehenden „funktionalen“ Ansätzen in der Programmierung. Vorher war Martin über 8 Jahre lang für IBM und 5 Jahre für Neofonie sowohl in Forschung und Entwicklung als auch in Kundenprojekten tätig. Er ist Master of Science Absolvent der HAW Hamburg in Informatik mit Schwerpunkt „Verteilte Systeme“.

ontolux bei der Buzzwords 2022

Qi Wu von der KI-Agentur ontolux stellt Methoden zur Ressourceneinsparung von KI-Modellen mittels Knowledge Destillation in ihrem Vortrag bei der Buzzwords 2022 in Berlin vor. Die Buzzwords Berlin konzentriert sich auf Themen wie Open-Source-Softwareprojekte, Suche, Speicherung, Streaming und Verarbeitung von großen Datenmengen.

Header_Vortrag_Qi Wu_ontolux_pietro Jeng-n6B49lTx7NM-unsplash_06_21

ontolux bei der Mind Mastering Machines 2022

Die Minds Mastering Machines (M3) Konferenz wendet sich an Softwareentwickler, Data Scientists und Forschende, die Machine Learning und KI-Anwendungen entwickeln. Qi Wu von der KI-Agentur ontolux stellt Methoden zur Ressourceneinsparung von KI-Modellen mittels Knowledge Destillation in einem Vortrag vor.

KIFE_042022

KI und Forschung 04-2022

Die nächste Runde der großen Machine Learning-Modelle ist eingeleitet und GPT-3 und T5 sollen in Rente geschickt werden. In der ontolux „KI und Forschung“- Reihe gibt Dr. Till Plumbaum wieder Einblicke in aktuelle KI-Themen.

Rennfahrer

Wie es B2B Webseiten gelingt, mehr Service zu bieten

Ein durchdachtes UX-Design verwandelt B2B-Websites in ein effizientes Service-Instrument. In der Marconomy gibt UX-Experte Axel Hillebrand Tipps, worauf B2B-Marketer achten sollten.

Kind und Karriere – Vereinbarkeit von Familie und Beruf bei Neofonie

Vereinbarkeit von Familie und Beruf ist für viele Eltern eine Herausforderung und erfordert familienfreundliche Unternehmenskulturen. Wie familienfreundlich ist Neofonie ? Wir haben Khayrat Glende, Head of Quality Management dazu befragt.

Entity Linking mit Doccano und TXTWerk

Zum Labeling von Entitäten hat ontolux das Open Source-Annotationstool Doccano mit der Anbindung an das hauseigene Textanalyse-Framework TXTWerk in einem Fork eingebunden, um eigene Entity Linking Annotationen zu erstellen.

Mit UX-Design die Candidate Experience optimieren

Erfahren Sie die zehn Must-Knows für ein optimales Karriereportal oder lassen Sie unsere Experten von ION ONE einen Blick auf Ihre Karriereseite werfen, um Optimierungspotenziale zu identifizieren für mehr Bewerber.

KIFE_3_2022

KI & Forschung 03-2022

Google bekommt Konkurrenz, doch bringen neue KI-gestützte Suchmaschinen wirklich eine Besserung für Nutzer? In unserer „KI und Forschung“- Reihe gibt Dr. Till Plumbaum wieder Einblick in aktuelle KI-Themen.

Viele Generationen – ein Team – Neofonie

Wir haben Joachim Schneider und Philip Kramwinkel befragt: Was eint und unterscheidet ihre Generationen und wie bereichern sie sich aus ihrer Sicht in der Zusammenarbeit bei Neofonie?

Neofonie gibt Praxistipps beim IHK Digitalisierungsforum

Um die regionale Wirtschaft bei Digitalisierungsfragen zielgerichtet unterstützen zu können, treffen sich die 79 bundesweit vertretenen Industrie und Handelskammern holen beim IHK Digitalisierungsforum in Dortmund. Neofonie ist als Partner der IHK Digital vor Ort und gibt Einblicke in die Praxis.

Neofonie ist neuer Contentful Partner

Contentful hat sich zu einem wichtigen Akteur für digital orientierte Unternehmen entwickelt, die ihre Inhalte optimal nutzen wollen, um ihren Kunden das beste digitale Erlebnis zu bieten. Neofonie nimmt Contentful in sein Partnerportfolio auf und unterstützt Unternehmen bei der Entwicklung und Implementierung.

Natural Language Processing mit dem Wikidata Knowledge Graph

Im Rahmen der Wikidata Data Reuse Days 2022 stellen Bertram Sändig und Patricia Helmich von ontolux ihre Erfahrungen vor, die sie beim Natural Language Processing mit dem Wikidata Knowledge Graph machen.

Header_KI_Forschung_ontolux_christopher-burns-Kj2SaNHG-hg-unsplash_06_21

KI & Forschung 02-2022

Wenn über künstliche Intelligenz gesprochen wird, kommen schnell auch ethische Überlegungen ins Spiel. In unserer aktuellen „KI und Forschung“- Reihe stellt Dr. Till Plumbaum die neuesten Entwicklungen zu den Themen Ethik und Bias vor.

Agile Führung – Bedeutung und Herausforderungen für Führungskräfte

Was agile Führung konkret ausmacht und welche Anforderungen an eine agile Führungskraft gestellt werden, klären wir im Interview mit Holger Paetsch – Head of Project Management bei der Digitalagentur Neofonie.

Neofonie-Mitarbeiter schauen sich Design-Entwürfe an

Wie eine Webseite zum Verkaufsinstrument wird

Erst mit einem durchdachten UX-Design verwandelt sich eine Webseite zu einem effizienten Verkaufsinstrument. Axel Hillebrand gibt fünf wesentliche Tipps.

IntelligenteSuche_Staubsauger_News

Fun: Was intelligente Suche und Staubsauger gemein haben

Angeblich stehen beutellose Staubsauger und das Thema Intelligente Suche nicht unbedingt in einem klaren inhaltlichen Zusammenhang. Warum eigentlich nicht?

TOP Arbeitgeber für IT-Jobs

Neofonie ist Top Arbeitgeber für IT-Jobs 2022

Die Zeitschrift CHIP hat zum zweiten Mal die „Top Arbeitgeber für IT-Jobs“ gekürt. Von mehr als 2.500 untersuchten Arbeitgebern konnte Neofonie überzeugen und zählt in der Softwarebranche zu den attraktivsten Arbeitgebern.

Header_KI_Forschung_ontolux_christopher-burns-Kj2SaNHG-hg-unsplash_01_22

KI & Forschung 01-2022

Dr. Till Plumbaum stellt auch 2022 die neuesten Forschungsergebnisse rund um die Themen NLP und Künstliche Intelligenz vor. In der aktuellen Januar-Ausgabe von „KI und Forschung“ geht es um Datenqualität und um die neueste Version von Lucene.

Newsbox_WomeninTech

Women in Tech bei Neofonie

Der Frauenanteil in der Neofonie liegt derzeit bei 31 Prozent, davon sind rund 25 Prozent direkt im Projektgeschäft aktiv. Diana Neufeld haben wir zum Thema Women in Tech bei Neofonie befragt.

newsbox-GWA_Win

Neofonie zählt zu den besten Web- und Online Agenturen 2022

Neofonie ist offizieller Preisträger der German Web Awards. Der Award kürt die besten Web- und Online Agenturen und zählt zu einer der wichtigsten Auszeichnungen in der DACH-Region.

Unser Newsletter „Neo Report“ vermittelt konkretes Praxiswissen, Trends und Know-how für Ihr digitales Business – quartalsweise und kompakt. Jetzt anmelden.