Gern stehe ich zur fachlichen Unterstützung in XML-Technologien, C#.NET, VisualBasic.NET und Java zur Verfügung. Sprechen Sie mich einfach an: Mail oder ☎ 0151 . 750 360 61


Lambda-Expressions in Java

Lambda-Expressions in Java

Lambda-Expressions in Java

➪ Konzepte und Elemente der funktionalen Programmierung halten immer mehr Einzug auch in Programmiersprachen, die als streng objektorientiert wahrgenommen werden. Seit Java 8 stehen in Java auch Lambda-Expressions zur Verfügung.

Auf dieser Seite:

Siehe auch und .

Interfaces und Default-Implementierung

Normalerweise dienen Interfaces dazu, abseits einer Vererbungshierarchie in allen Klassen, die das jeweilige Interface einbinden, die Implementierung einer Reihe von Methoden zu erzwingen. Dadurch wird die Programmierung wesentlich flexibler.

Seit Java 8 ist es möglich, in einem Interface auch eine Default-Implementierung vorzunehmen.


interface IMeinInterface {
  default void striche() {
    for(int i=0; i<45; i++) System.out.print("=");
    System.out.println();
  }
  default void ausgeben(String o)       { System.out.println(o); }
  default void ausgeben(int o)          { System.out.println(o); }
  default void ausgeben(double o)       { System.out.println(o); }
  default int gibAntwortAufAlleFragen() { return 42; }
  default int quadriere(int wert)       { return wert * wert; }
  default double quadriere(double wert) { return wert * wert; }
}

Das System erlaubt es nun, von diesem Interface eine Instanz zu erzeugen (auch ohne eine benannte Klasse, die das Interface implementiert, siehe x.getClass().getName()) und mit den Default-Methoden zu arbeiten.


public static void Interfacedemo() {
  IMeinInterface x = new IMeinInterface() {};
  x.striche();
  x.ausgeben("Hallo");
  x.striche();
  int a = x.gibAntwortAufAlleFragen();
  x.ausgeben("Die Antwort auf alle Fragen:");
  x.ausgeben(a);
  x.striche();
  a = x.quadriere(a);
  x.ausgeben("Die Quadratur der Antwort:");
  x.ausgeben(a);
  x.striche();
  x.ausgeben("Und noch etwas mit double");
  x.ausgeben(x.quadriere(12.6));
  x.striche();
}

Das Ergebnis sieht so aus:


=============================================
Hallo
=============================================
Die Antwort auf alle Fragen:
42
=============================================
Die Quadratur der Antwort:
1764
=============================================
Und noch etwas mit double
158.76
=============================================

Funktionale Interfaces (SAM)

Das soeben beschriebene Interface IMeinInterface enthält mehrere Methoden, die zudem noch eine Default-Implementierung aufweisen. Es eignet sich nicht zur Verwendung in Lambda-Expressions.

Wird in einem Interface dagegen nur eine einzige abstrakte Methode deklariert (ohne Default-Implementierung), so wird dieses auch funktional (Functional Interface Type) oder Single Abstract Method (SAM-Type) genannt. Sie können das Interface auch mit der @FunctionalInterface-Annotation markieren.


@FunctionalInterface
interface IStriche{
  void schreibe();
}

Anschließend implementieren Sie das Interface IStriche als Lambda-Expression. Gemeint sind damit Funktionen, die weder einen Return-Type noch einen Namen noch Exception-Deklarationen aufweisen. Rufen Sie dann schreibe auf.


public static void Lambdademo() {  
  IStriche x = () -> {
    for(int i=0; i<45; i++)
      System.out.print("=");
    System.out.println();
  };    
  x.schreibe();
}

Um Methoden mit verschiedenen Signaturen zu implementieren, müssten Sie normalerweise für jede Kombination ein eigenes Interface definieren und dieses implementieren.


interface IAusgeben{
  void ausgeben(Object o);
}
public static void Lambdademo() {  
  IAusgeben a = (p) -> {
    System.out.println(p);
  };
  a.ausgeben("Hallo");
}

Das können Sie leichter haben, wenn Sie ein Interface für alles Mögliche deklarieren:


@FunctionalInterface
interface IAllesMoegliche{
  Object run(Object o);
}

Dieses Interface können Sie nun nach Belieben für unterschiedliche Implementierungen verwenden ...


public static void Lambdademo() {
  IAllesMoegliche striche = (o) -> {
    for(int i=0; i<45; i++)
      System.out.print("=");
    System.out.println();
    return null;
  };
  IAllesMoegliche ausgeben = (o) -> {
    System.out.println(o);
    return null;
  };
  IAllesMoegliche AntwortAufAlles = (o) -> 42;
  IAllesMoegliche getInteger = (o) -> {
    return new Integer(76);
  };
  IAllesMoegliche quadriere = (o) -> {      
    int i = (int)o;
    return new Integer(i * i);
  };

... und die Programme aufrufen:


  striche.run(null);
  ausgeben.run("Moin");
  striche.run(null);
  int i = (int) getInteger.run(null);
  ausgeben.run(i);
  striche.run(null);
  i = (int) quadriere.run(i);
  ausgeben.run(i);
  striche.run(null);
}

Und so sieht das Ergebnis aus:


=============================================
Moin
=============================================
76
=============================================
5776
=============================================

Die Antwort auf alle Fragen kriegen Sie ebenso:


System.out.println(AntwortAufAlles.run(null));

Funktionale Interfaces in java.util.function

In java.util.function finden Sie mehrere funktionale Interfaces vordefiniert. Hier sehen Sie eine kurze Auswahl:

Function<T, R> Function definiert eine einzelne Parameterübergabe. T ist der übergebene Parametertyp, R ist der Rückgabetyp; Methode: apply.
Consumer<T> Consumer hat keinen Returnwert. <T>: ParameterTyp. Methode: accept.
Supplier<T> keine Parameterübergabe, <T> definiert Returntype. Methode: get.
Predicate<T> gibt einen Boolean-Wert zurück; <T>: ParameterTyp. Methode: test.

public static void Lambdademo2() {
  Function<Integer, Integer> fquadriere = wert -> wert * wert;
  Consumer<String> cSausgeben = wert -> System.out.println(wert);
  Consumer<Integer> cIausgeben = wert -> System.out.println(wert);
  Supplier<Integer> sAntwort = () -> 42;    
  Predicate<Integer> pIstgeradeZahl = wert -> wert % 2 == 0 ? true: false;    
  cSausgeben.accept("Hallo");    
  int y = sAntwort.get();
  cIausgeben.accept(y);    
  y = fquadriere.apply(y);    
  cIausgeben.accept(y);
  System.out.println(pIstgeradeZahl.test(y));
}

Verwendung von Lambda-Expressions in java.util.List

Ich bleibe vorerst bei IAllesMoegliche. Das Konzept können Sie auch dafür verwenden, via Lambda-Expression zuerst eine ArrayList zu füllen und diese dann (ebenfalls via Lambda) auszuwerten.


    // Fuelle die ArrayList mit Werten
    IAllesMoegliche fillList = (o) -> {
    	java.util.List<Integer> list;
        list=new java.util.ArrayList<Integer>();
    	for(int ii=5; ii<20; ii++) list.add(ii);
    	return list;
    };
    // Caste das von fillList.run zurueckgegebene
    // Objekt als java.util.List<Integer>,
    // sprich jedes darin befindliche Item als "wert" an
    // und gib es via Lambda aus.
    ((java.util.List<Integer>) fillList.run(null)).forEach( 
        (wert)->System.out.println(wert)         
    ); 

Interessant finde ich hier die parallele Auswertung ...


((java.util.List<Integer>) 
       fillList.run(null)).parallelStream().forEach(
               (wert)->System.out.println(wert)
);

und die hiermit erzielte interne Sortierung:


14
15
13
12
9
18
6
19
8
17
7
10
16
11
5

Diese Liste können Sie auch filtern (hier: alle durch 3 teilbare Zahlen):


((java.util.List<Integer>) fillList.run(null))
	    	.parallelStream()
	    	.filter(ww -> ww % 3 == 0)
	    	.forEach(
				(wert)->System.out.println(wert)
				);

mit dem Ergebnis:


15
6
9
18
12

map / filter / reduce

Die map-Funktion bildet jeden Wert auf einen anderen ab, nach einer eindeutigen Mappingvorschrift (hier: jeder Wert wird quadriert):


      ((java.util.List<Integer>) fillList.run(null))
        .stream()        
        .map(xx -> xx * xx)        
        .forEach(
        (wert)->System.out.println(wert)
        );

Die neue Liste quadrierter Werte können Sie filtern (hier: akzeptiere nur Werte, die ohne Rest durch 3 teilbar sind):


  ((java.util.List<Integer>) fillList.run(null))
      .stream()        
      .map(xx -> xx * xx)
      .filter(ww -> ww % 3 == 0)
      .forEach(
      (wert)->System.out.println(wert)
      );

Die gefilterte Liste (mittlerweile sind nur noch die Werte 36, 81, 144, 255 und 324 übrig, vgl. ) können Sie via reduce auf einen einzigen Wert reduzieren (hier habe ich die Einzelwerte paarweise addiert):


    Optional<Integer> reduzierterwert = 
        ((java.util.List<Integer>) fillList.run(null))
            .stream()        
            .map(xx -> xx * xx)
            .filter(ww -> ww % 3 == 0)
            .reduce((i1, i2) -> i1 + i2);
        System.out.println(reduzierterwert);

Das Ergebnis lautet hier:


Optional[810]

pic/java_lambda_map_reduce.png

Haben Sie schon mal auf diese Weise Durchschnittswerte berechnet?


double avg = ((java.util.List<Integer>) 
         fillList.run(null))
                 .stream()
                 .mapToInt(yy -> yy)
                 .average()
                 .getAsDouble();
System.out.println(avg);

HashMap und Lambda-Expressions

Sie mögen eine HashMap lieber? Auch die haben Sie mit Lambda im Griff (ohne sich mit einem iterator.hasNext() herumschlagen zu müssen):


java.util.Map<String, String> mymap;
mymap = new java.util.HashMap<String, String>();
mymap.put("H", "Hugo Holzflos");
mymap.put("S", "Siggi Sinnlos");
mymap.put("E", "Ella Endlos");
mymap.put("D", "Resi Denzschlos");
// Auswertung via Lambda
mymap.forEach((k,v) -> System.out.printf("%s\t%s\n", k, v));

Die Sortierung der Einträge erfolgt in der HashMap:


S	Siggi Sinnlos
D	Resi Denzschlos
E	Ella Endlos
H	Hugo Holzflos

wg / 20. November 2018



Fragen? Anmerkungen? Tipps?

Bitte nehmen Sie Kontakt zu mir auf.






Vielen Dank für Ihr Interesse an meiner Arbeit.


V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn

☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de

www.wilfried-grupe.de/Lambda_Java.html