C2G: Funktionen als Objekte behandeln
Ich kann Funktionen als First-Class Citizens behandeln: in Variablen speichern, als Rückgabewert verwenden und in Datenstrukturen ablegen.
Lernziele
| # | Lernziel | Beantwortet in |
|---|---|---|
| 1 | Ich kann eine Funktion in einer Variable speichern und diese Variable aufrufen, um die Funktion auszuführen. | 1. Funktion einer Variable zuweisen |
| 2 | Ich kann eine Funktion schreiben, die eine andere Funktion zurückgibt. | 2. Funktion als Rückgabewert |
| 3 | Ich kann Funktionen in Listen oder Dictionaries speichern und gezielt abrufen. | 3. Funktionen in Datenstrukturen speichern |
Überblick
In Java sind Funktionen keine eigenständigen Objekte wie in Python, aber mit funktionalen Interfaces (z.B. Function, UnaryOperator, BiFunction) können Lambdas und Methodenreferenzen in Variablen gespeichert, zurückgegeben und in Datenstrukturen abgelegt werden.
import java.util.function.*;
1. Funktion einer Variable zuweisen
Ein funktionales Interface mit genau einer abstrakten Methode kann eine Lambda-Expression oder Methodenreferenz aufnehmen.
// Lambda in einer Variable speichern
Function<String, String> greet = name -> "Hallo, " + name + "!";
// Über die Variable aufrufen
System.out.println(greet.apply("Anna")); // Hallo, Anna!
Methodenreferenz zuweisen
Bestehende Methoden können direkt als Referenz zugewiesen werden:
// Methodenreferenz statt Lambda
Function<String, String> toUpper = String::toUpperCase;
System.out.println(toUpper.apply("hallo")); // HALLO
Verschiedene funktionale Interfaces
// UnaryOperator: gleicher Typ für Ein- und Ausgabe
UnaryOperator<Integer> doubleIt = x -> x * 2;
System.out.println(doubleIt.apply(5)); // 10
// Predicate: gibt boolean zurück
Predicate<Integer> isEven = x -> x % 2 == 0;
System.out.println(isEven.test(4)); // true
System.out.println(isEven.test(7)); // false
// Consumer: nimmt einen Wert, gibt nichts zurück
Consumer<String> printer = System.out::println;
printer.accept("Hallo Welt"); // Hallo Welt
2. Funktion als Rückgabewert
Eine Methode kann eine Funktion (als funktionales Interface) zurückgeben. Das ermöglicht es, Funktionen dynamisch zu erzeugen.
// Gibt eine Funktion zurück, die mit dem gegebenen Faktor multipliziert
static UnaryOperator<Integer> createMultiplier(int factor) {
return x -> x * factor;
}
// Neue Funktionen erzeugen
var doubleIt = createMultiplier(2);
var tripleIt = createMultiplier(3);
System.out.println(doubleIt.apply(5)); // 10
System.out.println(tripleIt.apply(5)); // 15
System.out.println(doubleIt.apply(10)); // 20
Weiteres Beispiel: Begrüssung mit Präfix
static UnaryOperator<String> createGreeter(String prefix) {
return name -> prefix + " " + name + "!";
}
var formal = createGreeter("Guten Tag,");
var casual = createGreeter("Hey");
System.out.println(formal.apply("Herr Müller")); // Guten Tag, Herr Müller!
System.out.println(casual.apply("Max")); // Hey Max!
3. Funktionen in Datenstrukturen speichern
Funktionale Interfaces können in Listen oder Maps abgelegt und gezielt aufgerufen werden.
In einer Liste
// Drei Operationen als BiFunction speichern
record NamedOp(String name, BiFunction<Integer, Integer, Integer> fn) {}
var operations = List.of(
new NamedOp("add", (a, b) -> a + b),
new NamedOp("subtract", (a, b) -> a - b),
new NamedOp("multiply", (a, b) -> a * b)
);
for (var op : operations) {
System.out.println(op.name() + "(10, 3) = " + op.fn().apply(10, 3));
}
// add(10, 3) = 13
// subtract(10, 3) = 7
// multiply(10, 3) = 30
In einer Map
// Temperatur-Konverter in einer Map
var converters = Map.<String, UnaryOperator<Double>>of(
"fahrenheit", c -> c * 9 / 5 + 32,
"kelvin", c -> c + 273.15
);
// Gezielt abrufen und aufrufen
var unit = "kelvin";
var convert = converters.get(unit);
System.out.println("25°C in " + unit + ": " + convert.apply(25.0));
// 25°C in kelvin: 298.15
unit = "fahrenheit";
convert = converters.get(unit);
System.out.println("25°C in " + unit + ": " + convert.apply(25.0));
// 25°C in fahrenheit: 77.0
Das Map-Pattern ist besonders nützlich, um if/else-Ketten zu ersetzen:
// Statt:
double convert(double value, String unit) {
if (unit.equals("fahrenheit")) return value * 9 / 5 + 32;
else if (unit.equals("kelvin")) return value + 273.15;
else throw new IllegalArgumentException("Unknown unit: " + unit);
}
// Einfacher:
double convert(double value, String unit) {
return converters.get(unit).apply(value);
}