Agenda

  • Funktionale Programmierung
  • Lambdafunktionen
  • Allgemeine Funktionale Interfaces
  • Methodenreferenzen

Funktionale Programmierung

Funktionale Programmierung ist ein Programmierparadigma, bei dem Funktionen als Werte behandelt werden und auf Seiteneffekte verzichtet wird.

Funktionen als Werte

Funktionen...
  • sind Methoden
  • können als Parameter definiert werden
  • können als Argument definiert werden
  • können als Variable definiert werden

Seiteneffekt

Ein Seiteneffekt beschreibt eine Zustandsänderung

Beispiele Seiteneffekte

public class Human {
  private int age;
  
  public void setAge(age) {
    this.age = age;
    /*Seiteneffekt, da Wert außerhalb
     der Funktion verändert wird */ 
  }
  public int getAge() {
    return age;
    /*Kein Seiteneffekt, da Wert nicht außerhalb
     der Funktion verändert wird */ 
  }
}

Demo - Lambda Funktionen

*NKR

Lambdafunktionen

Lambdafunktion

Eine Lambdafunktion ist eine Methode ohne Name, die wie eine Referenzvariable verwendet werden kann.

public class Main {
  public static void main(String[] args) {
    Comparator<Human> sortAge;
    sortAge = (h1, h2) -> h1.age() > h2.age() ? 1 : -1;
  }
}
Lambdafunktionen werden auch anonyme Funktion, anonymous function oder arrow function genannt.

Typisierung

Ein funkionales Interface wird für die Typisierung einer Lambdafunktion verwendet.

public class Main {
  public static void main(String[] args) {
    Comparator<Human> sortAge;
    sortAge = (h1, h2) -> h1.age() > h2.age() ? 1 : -1;
  }
}

Ein funktionales Interface ist ein Interface mit genau einer abstrakten Methode und einer speziellen Annotation.

Funktionales Interface

Funktionale Interfaces werden mit @FunctionalInterface markiert, z.B. Comparator

@FunctionalInterface
public interface Comparator<T> {
  public int compare(T o1, T o2);
}

Nicht jedes Interface mit einer einzigen abstrakten Methode ist funktional, z.B. Comparable

Syntax Lambdafunktion

  • Kein Parameter
  • Ein Parameter
  • Mehrere Parameter
  • Eine Anweisung
  • Mehrere Anweisungen
  • Return Anweisung

Kein Parameter

Hat das funktionale Interface keinen Parameter, werden runde Klammern benötigt.

public interface NoParamFunction {
 public void do();
};
NoParamFunction function = () -> {
  System.out.println("Kein Parameter");
};

Ein Parameter

Hat das funktionale Interface einen Parameter, werden keine runden Klammern benötigt.

public interface OneParamFunction {
 public void do(String one);
}
OneParamFunction function = one -> {
  System.out.println("Ein Parameter: " + one);
};

Mehrere Parameter

Hat das funktionale Interface mehrere Parameter, werden runden Klammern benötigt.

public interface MultiParamFunction {
 public void do(String one, String two);
}
MultiParamFunction function = (one, two) -> {
  System.out.println("Zwei Parameter: " + one + two);
};

Eine Anweisung

Besteht die Lambdafunktion aus einer Anweisung sind keine geschweifte Klammern notwendig.

MultiParamFunction function = (one, two) -> 
  System.out.println("Zwei Parameter: " + one + two);

Mehrere Anweisungen

Besteht die Lambdafunktion aus mehrern Anweisungen sind geschweifte Klammern notwendig.

MultiParamFunction function = (one, two) -> {
  System.out.println("Parameter Eins: " + one);
  System.out.println("Parameter Zwei: " + two);
};

return-Anweisung

Besteht die Lambdafunktion aus einer einzelnen return Anweisung, sind keine geschweifte Klammern notwendig und das return Statement kann weggelassen werden.

public interface OneParamReturnFunction {
 public boolean validate(Human human);
}
OneParamReturnFunction function = h -> h.age() > 10;

Demo - Eigene Funktionale Interfaces

  • Intro Shopping List Example
  • Problem 1
  • Problem 2

Allgemeine Funktionale Interfaces

Grundkategorien von Funktionalen Interfaces

  • Consumer
  • Function
  • Predicate
  • Supplier

Consumer

public interface Consumer<T> {
  public void accept(T t);
}
public interface BiConsumer<T, U> {
  public void accept(T t, U u);
}

Code ausführen ohne Daten weiterzugeben.

Function

public interface Function<T, R> {
  public R apply(T t);
}
public interface BiFunction<T, U, R> {
  public R apply(T t, U u);
}
public interface UnaryOperator<T> {
  public T apply(T t);
}
public interface BinaryOperator<T> {
  public T apply(T t1, T t2);
}

Code ausführen, der Daten zurückgibt.

Predicate

public interface Predicate<T> {
  public boolean test(T t);
}

Code ausführen, der true oder false zurückgibt.

Supplier*

public interface Supplier<T> {
  public T get();
}

Code ausführen, der Daten vom Typ T zurückgibt.

*NKR

Demo - Allgemeine Funktionale Interfaces

  • Consumer anstatt ProductsChangedConsumer
  • Predicate anstatt AddAllowedChecker

Methodenreferenzen

Warum Methodenreferenzen?

Mit Methodenreferenzen kann man noch weniger Code schreiben.

Hat ein Parameter die gleiche Signatur, wie eine statische Methode, kann diese Methode als Methodenreferenz übergeben werden.

Beispiel ArrayList - For Each

public class Main {
  public static void main(String[] args) {
    ArrayList<String> names = new ArrayList<>()
    
    // lambda funktion
    names.forEach((name) -> System.out.println(name));
    
    // methodenreferenz
    names.forEach(System.out::println);
 }
}

Verwenden von Methodenreferenzen?

Anstatt die Methode über einen Punkt aufzurufen, wird ein zweifacher Doppelpunkt verwendet.

Mit dem "new" nach dem zweifachen Doppelpunkt kann auch der Konstruktor einer Klasse referenziert werden.

*NKR

Demo - Methodenreferenzen

  • Methodensignatur System.out.println
  • OneTimePrinter

Rest of the Day

Bei Lambdas 01 kann die Teilaufgabe mit anonymer Klasse ignoriert werden.