C3G: Einfache Lambda-Ausdrücke schreiben
Ich kann einfache Lambda-Ausdrücke für Berechnungen und String-Operationen schreiben, mit bedingten Ausdrücken kombinieren und erklären, wann Lambdas sinnvoll sind.
Lernziele
| # | Lernziel | Beantwortet in |
|---|---|---|
| 1 | Ich kann eine Lambda-Funktion schreiben, die eine einfache Berechnung durchführt (z.B. lambda x: x ** 2). | 1. Einzeilige Lambda-Funktion |
| 2 | Ich kann eine Lambda-Funktion schreiben, die eine String-Transformation durchführt (z.B. Grossbuchstaben-Konvertierung). | 2. Lambda mit String-Operation |
| 3 | Ich kann erklären, wann ein Lambda-Ausdruck sinnvoller ist als eine benannte Funktion und umgekehrt. | 3. Lambda vs. benannte Funktion |
| 4 | Ich kann einen Lambda-Ausdruck mit einem ternären Operator schreiben. | 4. Lambda mit bedingtem Ausdruck |
Überblick
Ein Lambda-Ausdruck in Java ist eine kompakte Schreibweise für eine anonyme Funktion. Lambdas implementieren ein funktionales Interface (ein Interface mit genau einer abstrakten Methode).
// Syntax
(parameter) -> ausdruck
(parameter) -> { anweisungen; }
Lambdas eignen sich für kurze, einmalige Operationen. Für alles Komplexere sollte man eine benannte Methode verwenden.
1. Einzeilige Lambda-Funktion
// Lambda für einfache Berechnungen
UnaryOperator<Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 25
UnaryOperator<Integer> doubleIt = x -> x * 2;
System.out.println(doubleIt.apply(7)); // 14
UnaryOperator<Integer> addTen = x -> x + 10;
System.out.println(addTen.apply(5)); // 15
Lambdas sind besonders praktisch, wenn sie direkt als Argument übergeben werden:
var numbers = List.of(1, 2, 3, 4, 5);
// Lambda direkt in sorted() verwenden
var descending = numbers.stream()
.sorted((a, b) -> b - a)
.toList();
System.out.println(descending); // [5, 4, 3, 2, 1]
2. Lambda mit String-Operation
// Grossbuchstaben
UnaryOperator<String> toUpper = s -> s.toUpperCase();
System.out.println(toUpper.apply("hallo")); // HALLO
// Oder als Methodenreferenz
UnaryOperator<String> toUpper2 = String::toUpperCase;
// String umkehren
UnaryOperator<String> reverse = s -> new StringBuilder(s).reverse().toString();
System.out.println(reverse.apply("Hallo")); // ollaH
Anwendung: Liste von Namen normalisieren
var names = List.of(" alice ", "BOB", " Charlie");
var cleaned = names.stream()
.map(name -> name.strip().substring(0, 1).toUpperCase()
+ name.strip().substring(1).toLowerCase())
.toList();
System.out.println(cleaned); // [Alice, Bob, Charlie]
3. Lambda vs. benannte Funktion
| Kriterium | Lambda | Benannte Methode |
|---|---|---|
| Umfang | Ein Ausdruck oder kurzer Block | Beliebig viele Zeilen |
| Name | Anonym | Sprechender Methodenname |
| Wiederverwendung | Für einmaligen Gebrauch | Für mehrfachen Aufruf |
| Lesbarkeit | Kurze, offensichtliche Logik | Komplexe oder benannte Logik |
| Debugging | Stacktrace zeigt lambda$... | Stacktrace zeigt Methodennamen |
Wann Lambda sinnvoll ist
// Gut: kurze, einmalige Sortierlogik
record Student(String name, double grade) {}
var students = List.of(
new Student("Anna", 2.1),
new Student("Ben", 1.5),
new Student("Clara", 1.8)
);
var sorted = students.stream()
.sorted((a, b) -> Double.compare(a.grade(), b.grade()))
.toList();
Wann eine benannte Methode besser ist
// Schlecht: Lambda zu komplex, schwer zu lesen
Function<Object, String> process = x ->
x instanceof String s
? s.strip().toLowerCase().replace(" ", "_")
: x.toString();
// Besser: benannte Methode
static String normalizeValue(Object x) {
if (x instanceof String s) {
return s.strip().toLowerCase().replace(" ", "_");
}
return x.toString();
}
Faustregel: Wenn die Lambda-Logik nicht auf einen Blick verständlich ist, sollte man eine benannte Methode verwenden.
4. Lambda mit bedingtem Ausdruck
Der ternäre Operator (bedingung ? wertWennWahr : wertWennFalsch) kann direkt in einem Lambda verwendet werden.
// Gerade oder ungerade?
Function<Integer, String> parity = x -> x % 2 == 0 ? "gerade" : "ungerade";
System.out.println(parity.apply(4)); // gerade
System.out.println(parity.apply(7)); // ungerade
// Positiv, negativ oder null?
Function<Integer, String> sign = x -> x > 0 ? "positiv" : (x == 0 ? "null" : "negativ");
System.out.println(sign.apply(5)); // positiv
System.out.println(sign.apply(0)); // null
System.out.println(sign.apply(-3)); // negativ
// Praktisches Beispiel: Rabatt berechnen
UnaryOperator<Double> discount = price -> price > 100 ? price * 0.9 : price;
System.out.println(discount.apply(150.0)); // 135.0
System.out.println(discount.apply(80.0)); // 80.0
Anwendung in map()
var temperatures = List.of(35, 28, 42, 15, 38);
var warnings = temperatures.stream()
.map(t -> t + "°C: " + (t > 35 ? "WARNUNG" : "OK"))
.toList();
System.out.println(warnings);
// [35°C: OK, 28°C: OK, 42°C: WARNUNG, 15°C: OK, 38°C: WARNUNG]