Functioneel programmeren met Lambda expressies in Java 8

java 8 lambdaJava is een object-georiënteerde taal: een Java programma wordt uitgevoerd door objecten die met elkaar samenwerken. De meeste objecten zijn specialisten, met eigen gegevens, en methoden om die gegevens te benaderen. Er bestaan ook objecten zonder eigen state, die alleen bedoeld zijn om functionaliteit door te geven aan een ander object. Tot voor kort was er veel “boilerplate” code nodig om dergelijke functionele objecten te gebruiken: de meeste code had betrekking op het inpakken van de gewenste functionaliteit in een object. Sinds Java 8 maken Lambda expressies het mogelijk om functies veel directer door te geven.

In dit betoog wordt het nut van Lambda expressies geïllustreerd. We sorteren een lijst artikelen eerst op de Java 7 manier  en vervolgens met Lambda expressies.

Bij het sorteren van een collectie kunnen we een Comparator object meegeven aan de sorteermethode. Dit object geeft aan hoe gesorteerd moet worden.  (Vaak kennen objecten daarnaast ook een “natuurlijke” sorteervolgorde door de interface Comparable te implementeren)

Artikelen sorteren in Java 7

In het volgende voorbeeld zullen we een lijst artikelen op verschillende manieren sorteren. De klasse Artikel heeft een naam en een prijs (in dit verkorte voorbeeld niet private gedeclareerd).

Java8-1

De toString() methode wordt gebruikt om een Artikel object als tekst weer te geven, in dit geval eerst de naam, dan de prijs.

In de klasse Application worden artikelen in een collectie gestopt:

Java8-2

De collectie is nu nog niet gesorteerd, de uitvoer ziet er als volgt uit:

Java8-3

Om de collectie te kunnen sorteren op prijs hebben we klasse nodig die de interface Comparator implementeert. De compare() methode van Comparator wordt tijdens het sorteren gebruikt om de volgorde van artikelen te bepalen.

 

Java8-4

We hebben een object van de zojuist gemaakte PrijsComparator klasse nodig om de collectie te sorteren:

Java8-5

De artikelen zijn nu op prijs gesorteerd:

Java8-6

Anonieme inner klasse

Merk op dat het object van de klasse PrijsComparator maar op één plek wordt gebruikt. In dit soort gevallen wordt vaak een anonieme inner klasse gebruikt, op de plek van de sortering zelf. De klasse PrijsComparator wordt dan overbodig, maar het blijft veel code, en moeilijk leesbaar:

Java8-7

Wat als we de lijst met artikelen later ook op naam willen sorteren? Dan zal veel code herhaald moeten worden:

Java8-8

De tweede sortering maakt gebruik van de methode compareTo() van String om artikelen op volgorde van naam te kunnen sorteren.


 Comparator implementeren met Java 8 Lambda expressies

Sinds Java 8 heeft Oracle Lambda expressies aan de taal toegevoegd. Deze maken het mogelijk om de gewenste functionaliteit direct als argument door te geven aan een methode. De stap van het maken van een object laten we hierbij over aan de compiler.

In voorgaande voorbeelden hebben we de methode  Collections.sort(List<Artikel> list, Comparator<Artikel> comparator) gebruikt. Een slimme compiler kan veel informatie afleiden uit deze signatuur:

  1. De te sorteren lijst bevat elementen van de klasse Artikel;
  2. Het tweede argument van de sort() methode moet dus een object van het type Comparator<Artikel> zijn;
  3. Dit object heeft de methode compare(), met twee Artikel argumenten en een int als return type.

De enige informatie die de compiler nog niet zelf kan afleiden is de inhoud van de compare() methode. Deze kunnen we sinds Java 8 direct bij het aanroepen van de sort methode als Lambda expressie meegeven:

 Java8-9

We zien hier dat als tweede argument van de sort methode een expressie wordt meegegeven die begint met (a1, a2). Dit zijn de argumenten van de compare() methode. Het feit dat de compiler al weet dat ze van het type Artikel zijn wordt type inference genoemd. Achter de pijl -> staat de body van de compare methode. De expressie die hierin staat moet overeenkomen met het return type van de methode (in dit geval: int).

Op de achtergrond bouwt de compiler zelf de bijbehorende anonieme inner klassen op. De ontwikkelaar hoeft nu alleen de gewenste functionaliteit door te geven.

Methode referenties

Als body van een Lambda expressie kan ook gebruik worden gemaakt van bestaande methoden. Stel dat we de Artikel klasse uitbreiden met een methode die artikelen sorteert op naam:

Java8-10

In een Lambda expressie kunnen we deze methode aanroepen.

Java8-11 

De compiler is nu zo slim om direct te zien dat de argumenten van de compare() methode van Comparator overeenkomen met de argumenten van de perNaam() methode. Semantisch is deze expressie hetzelfde als:

 

Java8-12


Functionele interfaces
Interfaces die één abstracte methode hebben kunnen vanaf Java 8 gebruik maken van Lambda expressies. Dat kan gaan om reeds bekende interfaces, zoals Comparator  met de methode compare() en Runnable met de methode run(). Daarnaast bevat de nieuwe package java.util.function diverse nieuwe functionele interfaces met handige, duidelijk beschreven functionaliteit, zoals:

interface Function <T, R> { R apply(T t);} –  een functie met 1 parameter van type T, en een result type van type R;

interface Predicate<T>{ boolean test(T t); } – een functie die true of false geeft bij een bepaald argument van type T;

interface Consumer<T> { void accept(T t); } – een functie die een operatie uitvoert op een parameter zonder iets terug te geven.

Dergelijke interfaces kunnen als parameters worden gebruikt voor methoden. De implementatie van zo’n interface kan dan weer met behulp van een Lambda expressie worden doorgegeven.

 

Tot slot
De genoemde voorbeelden in dit artikel lichten slechts een tipje van de sluier op van wat Lambda expressies en functioneel programmeren in Java 8 te bieden hebben. Deze verkorte schrijfwijze voegt inhoudelijk misschien weinig toe: met meer code was het in voorgaande versies ook mogelijk. Toch is het een belangrijke stap voorwaarts, omdat Lambda expressies ontwikkelaars helpen om zich meer te richten op de inhoud, de functionaliteit, en minder op de vorm, die de compiler nu zelf kan afleiden.

 

 

Onderwerpen
Actieve filters: Wis alle filters
Pageloader
Algemene voorwaarden

Jouw persoonsgegevens worden opgenomen in onze beschermde database en worden niet aan derden verstrekt. Je stemt hiermee in dat wij jou van onze aanbiedingen op de hoogte houden. In al onze correspondentie zit een afmeldmogelijkheid