/
/
·
·
/
/
·
·
  • Einführung
  • Die Idee in einem Satz
  • Vor Java 8: anonyme [Klassen](/themen/klassen-objekte)
  • Lambda-Syntax
  • Funktionale Interfaces
  • Higher-Order-Methoden: map, filter, forEach
  • Method-References: noch kürzer
  • Functional Composition
  • Klausur-Faustregeln
  • Häufige Stolpersteine
  • Klausur-Beispielaufgabe
ThemenProgrammiergrundlagenLambdas & Higher-Order-Funktionen
Programmiergrundlagen·4Lerneinheiten·37min·Stand17.07.2026

Lambdas & Higher-Order-Funktionen.

Stell dir vor du willst eine Liste sortieren, aber nicht alphabetisch, sondern nach Notenschnitt, oder nur die ungeraden Zahlen behalten, oder jede Zahl verdoppeln. Du müsstest jedes Mal eine neue Funktion schreiben. Lambdas sind Mini-Funktionen, die du wie Werte weitergeben kannst, und so beliebige Logik in fertige Methoden einbauen. In Java wird ein Lambda dabei immer als Instanz eines funktionalen Interfaces verwendet, zum Beispiel Predicate für eine Filter-Bedingung oder Function für eine Transformation: der Zieltyp links bestimmt, welche Methode das Lambda implementiert. Praktisch zahlt sich das in Klausuren dreifach aus: du kannst Stream-Ausgaben Schritt für Schritt bestimmen, eine Lambda-Syntax als korrekt oder fehlerhaft erkennen und ein Lambda dem passenden funktionalen Interface zuordnen.

Seit Java 8 (2014) Teil der Sprache, heute also Standard und kein Spezialwissen mehr. Klausur-Vorkommen in rund 5 von 13 gesichteten WInf-Prog-1-Klausuren (eigene, anonymisierte Klausur-Sammlung deutschsprachiger Hochschulen, Zeitraum ca. 2020 bis 2025; P3, Vertiefung, in Prog-2 deutlich häufiger).

Lambda: In Java eine anonyme Funktionsbeschreibung, die über ein funktionales Interface typisiert wird, und die du wie einen Wert behandeln kannst, speichern, weitergeben, später ausführen. Higher-Order-Funktion: Eine Funktion, die andere Funktionen als Parameter nimmt oder zurückgibt.

Die klassische Lösung war eine anonyme Klasse:

List<String> namen = Arrays.asList("Charlie", "Alice", "Bob");

Collections.sort(namen, new Comparator<String>() {
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

5 Zeilen für "sortiere nach Länge". Mit Lambda:

Collections.sort(namen, (a, b) -> a.length() - b.length());

Von 5 Zeilen auf 1 Zeile, gleicher Effekt. Das ist der Hauptvorteil von Lambdas: Prägnanz ohne Funktionsverlust.

// Klassisch (3 Varianten):
(int a, int b) -> { return a + b; }   // mit Typ-Annotation + Block
(a, b) -> { return a + b; }           // Typen inferiert
(a, b) -> a + b                       // Single-Expression, return implizit

Faustregeln:

  • 1 Parameter? Klammern weglassen: x -> x * 2
  • 1 Expression? Klammern + return weg: (a, b) -> a + b
  • Mehrere Statements? Block + return nötig: (a, b) -> { int s = a + b; return s; }

Ein Lambda implementiert immer ein funktionales Interface, ein Interface mit genau einer abstrakten Methode (default- und static-Methoden im Interface zählen dabei nicht mit, sie haben ja schon einen Rumpf). Das ist ein typischer Klausur-Fallstrick: ein Interface mit einer abstrakten plus drei default-Methoden ist immer noch funktional.

Die wichtigsten für den Einstieg sind Function, Predicate, Consumer und Supplier, alle aus dem Paket java.util.function. Diese vier Karten oben musst du in der Klausur abrufbar haben, sie decken Transformation, Test, Aktion und Erzeugung ab. Daneben gibt es Spezialvarianten, die du nur bei Bedarf nachschlägst: BiFunction für zwei Eingaben, UnaryOperator als Function mit gleichem Ein-/Ausgabetyp, primitive Varianten wie IntPredicate.

InterfaceSignaturEingang → AusgangWofür
Function<T, R>R apply(T t)T → RTransformation (z. B. map)
Predicate<T>boolean test(T t)T → booleanFilter-Bedingung (z. B. filter)
Consumer<T>void accept(T t)T → (nichts)Aktion mit Seiteneffekt (z. B. forEach)
Supplier<T>T get()(nichts) → TErzeuger ohne Eingabe
BiFunction<T,U,R>R apply(T t, U u)T, U → Rzwei Eingaben → ein Ergebnis

Diese Tabelle ist deine Signatur-Karte: links das Interface, in der Mitte was rein- und rausgeht, rechts die passende Stream-Operation. Lies eine Zeile immer als Pfeil, dann ordnest du jedes Lambda blitzschnell zu.

Beispiele:

Function<Integer, Integer> verdoppeln = x -> x * 2;
verdoppeln.apply(5);  // 10

Predicate<Integer> isGerade = n -> n % 2 == 0;
isGerade.test(4);  // true

Consumer<String> drucker = s -> System.out.println(s);
drucker.accept("Hallo");  // gibt Hallo aus

Listen / Streams haben Methoden, die andere Funktionen als Argument nehmen. Höhere Ordnung = sie verarbeiten Funktionen. Stell dir die Kette als Fließband vor, jede Stufe bekommt eine Liste rein und gibt eine Liste (oder einen Wert) raus:

[1,2,3,4,5,6]
   |
   v  filter (gerade/ungerade)   -> wirft Elemente raus
[2,4,6]
   |
   v  map (x*2 / x*x)            -> verändert jedes Element
[4,16,36]
   |
   v  reduce (Summe / Produkt)   -> kombiniert alle zu einem Wert
56

Genau diese drei Stufen schaltest du im Visualizer weiter unten per Checkbox dazu oder weg und siehst die Zwischen-Liste nach jeder Stufe.

List<Integer> zahlen = List.of(1, 2, 3, 4, 5);

// filter: behalte nur die geraden
zahlen.stream()
    .filter(n -> n % 2 == 0)        // [2, 4]
    .forEach(System.out::println);

// map: verdoppele jede Zahl
List<Integer> verdoppelt = zahlen.stream()
    .map(n -> n * 2)                // [2, 4, 6, 8, 10]
    .collect(Collectors.toList());

// reduce: alle aufsummieren
int summe = zahlen.stream()
    .reduce(0, (acc, n) -> acc + n); // 15

Faustregel:

  • filter, wirft Elemente raus
  • map, verändert jedes Element
  • reduce, kombiniert alle zu einem
  • forEach, macht etwas mit jedem (Seiteneffekt)

Eine kombinierte Pipeline löst du am sichersten, indem du nach jeder Operation die Zwischen-Liste hinschreibst. So liest sich Stream.of(1,2,3,4,5,6).filter(n -> n % 2 == 0).map(n -> n * n).reduce(0, Integer::sum) von links nach rechts:

Start:   [1, 2, 3, 4, 5, 6]
filter (n gerade)   ->  [2, 4, 6]
map    (n * n)      ->  [4, 16, 36]
reduce (0, +)       ->  4 + 16 + 36 = 56

In echt werden Streams lazy ausgewertet: Java baut keine echten Zwischen-Listen, sondern schiebt jedes Element einzeln durch die ganze Kette. Die Zwischen-Listen oben sind nur deine Denkstütze für die Klausur, das Ergebnis ist exakt dasselbe.

Reihenfolge zählt: filter VOR map ist effizienter, weil map dann weniger Elemente transformieren muss. Vertauscht (erst map, dann filter) kommt hier zwar dasselbe Ergebnis raus, aber das ist nicht garantiert, sobald filter auf das Original-Element prüft.

Manchmal ist das Lambda nur ein Aufruf einer existierenden Methode. Dann gibt's eine Kurzschreibweise mit :::

// Lambda
list.forEach(s -> System.out.println(s));

// Method-Reference (identisch)
list.forEach(System.out::println);

// String::length statt s -> s.length()
list.stream().map(String::length);

Lesbarer in vielen Fällen, aber nicht überall sinnvoll, bei nicht-trivialen Lambdas lieber explizit.

Funktionen lassen sich verketten:

Function<Integer, Integer> verdoppeln = x -> x * 2;
Function<Integer, Integer> plusZehn = x -> x + 10;

Function<Integer, Integer> kombi = verdoppeln.andThen(plusZehn);
kombi.apply(3);  // (3 * 2) + 10 = 16

Function<Integer, Integer> kombi2 = verdoppeln.compose(plusZehn);
kombi2.apply(3);  // (3 + 10) * 2 = 26

andThen: erst die eine, dann die andere. compose: umgekehrt (innere zuerst).

1. Lambda implementiert ein funktionales Interface. Genau EINE abstrakte Methode. Die wichtigsten für den Einstieg sind Function, Predicate, Consumer und Supplier aus java.util.function; weitere Spezialvarianten gibt es, brauchst du aber selten auswendig.

2. Lambda-Syntax kennen. Single-Param: keine Klammern (x -> x*2). Single-Expression: kein return, kein Block ((a,b) -> a+b).

3. Higher-Order-Methoden machen mehr mit weniger Code. map/filter/reduce sind funktionale Standard-Operationen.

4. Method-Reference :: ist Syntax-Sugar für ein Lambda das eine bestehende Methode aufruft. System.out::println statt s -> System.out.println(s).

5. Lambdas sind nicht Threads. Ein Lambda läuft NICHT automatisch parallel, es ist nur eine Funktion. Für Parallelität: stream().parallel() oder ExecutorService.

1. return vergessen bei mehrzeiligem Body. (a, b) -> { a + b } ist Compile-Fehler, entweder (a, b) -> a + b oder (a, b) -> { return a + b; }.

2. Effectively-final-Regel. Lambda kann nur lokale Variablen lesen, die effectively final sind (nicht verändert nach der Zuweisung). Innerhalb des Lambdas reassign → Compile-Error.

int x = 10;
Function<Integer, Integer> f = n -> n + x;  // ok
x = 20;  // ← x ist nicht mehr effectively final → Compile-Error oben

3. Stream nicht 2x nutzen. Ein Stream ist EINMAL nutzbar. stream.filter(...).count() und dann nochmal stream.forEach(...) → IllegalStateException.

4. null in Streams. null kann durch filter/map laufen und Probleme später machen. Vorher .filter(Objects::nonNull) als Schutz.

5. Lambda mit Seiteneffekt in map/filter. map und filter sollten pur sein, also nebenwirkungsfrei (= keinen externen State ändern, nur aus den Eingaben ein Ergebnis zurückgeben). Wenn du Seiteneffekte willst → forEach am Ende.

Beispiel (typischer Klausur-Stil, zur Übung konstruiert).

Gegeben sei folgender Code:

List<Integer> werte = List.of(1, 2, 3, 4, 5, 6);

int ergebnis = werte.stream()
    .filter(n -> n % 2 == 1)
    .map(n -> n * n)
    .reduce(0, Integer::sum);

(a) Welchen Wert hat ergebnis? Gib die Zwischen-Liste nach jeder Operation an. (b) Welches funktionale Interface erwartet filter, welches map? (c) Das folgende Lambda kompiliert nicht, korrigiere es: (a, b) -> { a + b }. (d) Schreibe werte.forEach(x -> System.out.println(x)) mit einer Method-Reference.

Lösung:

  • (a) Schrittweise (siehe Abschnitt Higher-Order-Methoden): filter (ungerade) → [1, 3, 5], map (quadrieren) → [1, 9, 25], reduce mit Summe → 1 + 9 + 25 = 35. Also ergebnis = 35.
  • (b) filter erwartet ein Predicate<Integer> (T → boolean), map eine Function<Integer, Integer> (T → R). Beides steht in der Signatur-Karte im Abschnitt Funktionale Interfaces.
  • (c) Der Block-Body { ... } braucht ein return (siehe Lambda-Syntax und Stolperstein 1). Korrekt: (a, b) -> a + b (Single-Expression, return implizit) oder (a, b) -> { return a + b; }.
  • (d) werte.forEach(System.out::println) (siehe Method-References). Funktional identisch zum Lambda.

Der Visualizer startet mit der festen Liste [1, 2, 3, 4, 5, 6]. Du aktivierst oder deaktivierst per Checkbox drei Operationen und siehst nach jeder Stufe die Zwischen-Liste und am Ende das Endergebnis:

  • filter, wahlweise gerade oder ungerade behalten
  • map, wahlweise verdoppeln (n * 2) oder quadrieren (n * n)
  • reduce, wahlweise Summe oder Produkt

Schalte Operationen einzeln dazu und beobachte, wie sich die Liste nach jeder Stufe verändert. So entwickelst du Intuition für filter, map, reduce und ihre Zusammenarbeit, genau das brauchst du für Stream-Auswertungs-Aufgaben.

Lade Visualisierung...

Klausur-Tipp: Bei "Was gibt der folgende Stream zurück?", geh Operation für Operation durch. filter wirft raus, map verändert, reduce kombiniert. Notiere dir nach jeder Operation die Zwischen-Liste aufs Papier, nie nur im Kopf zusammenfassen, das ist die häufigste Fehlerquelle in Stream-Aufgaben.


Drei Demos zum Durchklicken. Klick Step oder Auto und beobachte, wie sich Variablen, Zwischen-Listen und Konsole pro Schritt verändern. Die Annotation unten am Code erklärt, was in diesem Step passiert.

1. Stream-Kette: filter, map, reduce auswerten

Die Klausur-Standardaufgabe: eine Stream-Kette von Hand auswerten. Hier laufen [1..6] durch filter (gerade), map (quadrieren) und reduce (Summe). Achte darauf, wie die Liste nach jeder Operation schrumpft oder sich verwandelt.

Lade Visualisierung...

2. Funktionales Interface: Predicate per Lambda

Ein Lambda ist nichts Magisches, es ist eine Instanz eines funktionalen Interfaces. Hier wird ein Predicate<Integer> durch ein Lambda gebaut und dann zweimal mit test(...) aufgerufen. Die abstrakte Methode test wird direkt durch den Lambda-Rumpf implementiert.

Lade Visualisierung...

3. Effectively-final: warum das Reassign bricht

Ein Lambda darf lokale Variablen nur lesen, wenn sie effectively final sind. Sobald die Variable nach der Zuweisung noch verändert wird, ist sie es nicht mehr, und die Zeile mit dem Lambda wird zum Compile-Error. Hier siehst du, an welcher Stelle der Compiler abbricht.

Lade Visualisierung...

Drei Faustregeln, die in Stream- und Lambda-Aufgaben fast immer Punkte bringen:

  1. Stream-Auswertung schrittweise: Bei jeder Stream-Kette jede Operation einzeln durchgehen (filter → map → reduce) und die Zwischen-Liste notieren. Nie im Kopf zusammenfassen, das ist die häufigste Fehlerquelle.
  2. Effectively-final-Regel: Ein Lambda kann lokale Variablen nur lesen, wenn sie nach der Zuweisung nicht mehr verändert werden. Ein Reassign der Variable macht die Lambda-Zeile zum Compile-Error.
  3. Method-Reference :: ist Syntax-Zucker: System.out::println und s -> System.out.println(s) sind funktional identisch und in der Klausur austauschbar. Lass dich von :: nicht verwirren, übersetze es im Kopf ins gleichwertige Lambda.

Anmelden, um den Fortschritt zu speichern.

Nächster Schritt

Wenn du fertig bist: jetzt üben.

Aktives Abrufen festigt Wissen schneller als nochmal lesen.

War das hilfreich?

Verwandte Themen

  • Variablen und Datentypen
  • Bedingungen
  • Schleifen
  • Funktionen
  • Rekursion

Folgt

  • Streams und Collectors
  • Threads und Nebenläufigkeit
  • GUI mit JavaFX

Tools

Bald: Karteikarten · Spaced-Repetition · Mind-Map-Export

Fachliche Qualität
S-Tier · GoldstandardZuletzt geprüft am 11.06.2026

Diese Lerneinheit wurde für typische Bachelor-Klausuren konzipiert. So prüfen wir · Fehler entdeckt? Melde ihn uns oder markiere die fragliche Stelle direkt im Text oben.

Klausur-ÜbersichtKomplette Übersicht: alle Tabs als linearer Text zum Lernen
▾

Alle Tabs der Lerneinheit (Erklärung · Interaktiv verstehen · Praxis-Übung · Klausur-Quiz) als durchgehender Text. Ideal zum Wiederholen vor der Klausur, und für Suchmaschinen wie Google, Bing und KI-Suche (ChatGPT, Perplexity).

Inhalt dieser Übersicht

  1. Erklärung(Erklärung)
  2. Interaktiv verstehen(Visualisierung / Interaktiv)
  3. Praxis-Übung(Quiz / Klausurfragen)
  4. Klausur-Quiz(Quiz / Klausurfragen)
Teil 1·Erklärung

Erklärung

Stell dir vor du willst eine Liste sortieren, aber nicht alphabetisch, sondern nach Notenschnitt, oder nur die ungeraden Zahlen behalten, oder jede Zahl verdoppeln. Du müsstest jedes Mal eine neue Funktion schreiben. Lambdas sind Mini-Funktionen, die du wie Werte weitergeben kannst, und so beliebige Logik in fertige Methoden einbauen. In Java wird ein Lambda dabei immer als Instanz eines funktionalen Interfaces verwendet, zum Beispiel Predicate für eine Filter-Bedingung oder Function für eine Transformation: der Zieltyp links bestimmt, welche Methode das Lambda implementiert. Praktisch zahlt sich das in Klausuren dreifach aus: du kannst Stream-Ausgaben Schritt für Schritt bestimmen, eine Lambda-Syntax als korrekt oder fehlerhaft erkennen und ein Lambda dem passenden funktionalen Interface zuordnen.

Seit Java 8 (2014) Teil der Sprache, heute also Standard und kein Spezialwissen mehr. Klausur-Vorkommen in rund 5 von 13 gesichteten WInf-Prog-1-Klausuren (eigene, anonymisierte Klausur-Sammlung deutschsprachiger Hochschulen, Zeitraum ca. 2020 bis 2025; P3, Vertiefung, in Prog-2 deutlich häufiger).

Die Idee in einem Satz

Lambda: In Java eine anonyme Funktionsbeschreibung, die über ein funktionales Interface typisiert wird, und die du wie einen Wert behandeln kannst, speichern, weitergeben, später ausführen. Higher-Order-Funktion: Eine Funktion, die andere Funktionen als Parameter nimmt oder zurückgibt.

Vor Java 8: anonyme Klassen

Die klassische Lösung war eine anonyme Klasse:

List<String> namen = Arrays.asList("Charlie", "Alice", "Bob");

Collections.sort(namen, new Comparator<String>() {
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

5 Zeilen für "sortiere nach Länge". Mit Lambda:

Collections.sort(namen, (a, b) -> a.length() - b.length());

Von 5 Zeilen auf 1 Zeile, gleicher Effekt. Das ist der Hauptvorteil von Lambdas: Prägnanz ohne Funktionsverlust.

Lambda-Syntax

// Klassisch (3 Varianten):
(int a, int b) -> { return a + b; }   // mit Typ-Annotation + Block
(a, b) -> { return a + b; }           // Typen inferiert
(a, b) -> a + b                       // Single-Expression, return implizit

Faustregeln:

  • 1 Parameter? Klammern weglassen: x -> x * 2
  • 1 Expression? Klammern + return weg: (a, b) -> a + b
  • Mehrere Statements? Block + return nötig: (a, b) -> { int s = a + b; return s; }

Funktionale Interfaces

Ein Lambda implementiert immer ein funktionales Interface, ein Interface mit genau einer abstrakten Methode (default- und static-Methoden im Interface zählen dabei nicht mit, sie haben ja schon einen Rumpf). Das ist ein typischer Klausur-Fallstrick: ein Interface mit einer abstrakten plus drei default-Methoden ist immer noch funktional.

Die wichtigsten für den Einstieg sind Function, Predicate, Consumer und Supplier, alle aus dem Paket java.util.function. Diese vier Karten oben musst du in der Klausur abrufbar haben, sie decken Transformation, Test, Aktion und Erzeugung ab. Daneben gibt es Spezialvarianten, die du nur bei Bedarf nachschlägst: BiFunction für zwei Eingaben, UnaryOperator als Function mit gleichem Ein-/Ausgabetyp, primitive Varianten wie IntPredicate.

InterfaceSignaturEingang → AusgangWofür
Function<T, R>R apply(T t)T → RTransformation (z. B. map)
Predicate<T>boolean test(T t)T → booleanFilter-Bedingung (z. B. filter)
Consumer<T>void accept(T t)T → (nichts)Aktion mit Seiteneffekt (z. B. forEach)
Supplier<T>T get()(nichts) → TErzeuger ohne Eingabe
BiFunction<T,U,R>R apply(T t, U u)T, U → Rzwei Eingaben → ein Ergebnis

Diese Tabelle ist deine Signatur-Karte: links das Interface, in der Mitte was rein- und rausgeht, rechts die passende Stream-Operation. Lies eine Zeile immer als Pfeil, dann ordnest du jedes Lambda blitzschnell zu.

Beispiele:

Function<Integer, Integer> verdoppeln = x -> x * 2;
verdoppeln.apply(5);  // 10

Predicate<Integer> isGerade = n -> n % 2 == 0;
isGerade.test(4);  // true

Consumer<String> drucker = s -> System.out.println(s);
drucker.accept("Hallo");  // gibt Hallo aus

Higher-Order-Methoden: map, filter, forEach

Listen / Streams haben Methoden, die andere Funktionen als Argument nehmen. Höhere Ordnung = sie verarbeiten Funktionen. Stell dir die Kette als Fließband vor, jede Stufe bekommt eine Liste rein und gibt eine Liste (oder einen Wert) raus:

[1,2,3,4,5,6]
   |
   v  filter (gerade/ungerade)   -> wirft Elemente raus
[2,4,6]
   |
   v  map (x*2 / x*x)            -> verändert jedes Element
[4,16,36]
   |
   v  reduce (Summe / Produkt)   -> kombiniert alle zu einem Wert
56

Genau diese drei Stufen schaltest du im Visualizer weiter unten per Checkbox dazu oder weg und siehst die Zwischen-Liste nach jeder Stufe.

List<Integer> zahlen = List.of(1, 2, 3, 4, 5);

// filter: behalte nur die geraden
zahlen.stream()
    .filter(n -> n % 2 == 0)        // [2, 4]
    .forEach(System.out::println);

// map: verdoppele jede Zahl
List<Integer> verdoppelt = zahlen.stream()
    .map(n -> n * 2)                // [2, 4, 6, 8, 10]
    .collect(Collectors.toList());

// reduce: alle aufsummieren
int summe = zahlen.stream()
    .reduce(0, (acc, n) -> acc + n); // 15

Faustregel:

  • filter, wirft Elemente raus
  • map, verändert jedes Element
  • reduce, kombiniert alle zu einem
  • forEach, macht etwas mit jedem (Seiteneffekt)

Eine kombinierte Pipeline löst du am sichersten, indem du nach jeder Operation die Zwischen-Liste hinschreibst. So liest sich Stream.of(1,2,3,4,5,6).filter(n -> n % 2 == 0).map(n -> n * n).reduce(0, Integer::sum) von links nach rechts:

Start:   [1, 2, 3, 4, 5, 6]
filter (n gerade)   ->  [2, 4, 6]
map    (n * n)      ->  [4, 16, 36]
reduce (0, +)       ->  4 + 16 + 36 = 56

In echt werden Streams lazy ausgewertet: Java baut keine echten Zwischen-Listen, sondern schiebt jedes Element einzeln durch die ganze Kette. Die Zwischen-Listen oben sind nur deine Denkstütze für die Klausur, das Ergebnis ist exakt dasselbe.

Reihenfolge zählt: filter VOR map ist effizienter, weil map dann weniger Elemente transformieren muss. Vertauscht (erst map, dann filter) kommt hier zwar dasselbe Ergebnis raus, aber das ist nicht garantiert, sobald filter auf das Original-Element prüft.

Method-References: noch kürzer

Manchmal ist das Lambda nur ein Aufruf einer existierenden Methode. Dann gibt's eine Kurzschreibweise mit :::

// Lambda
list.forEach(s -> System.out.println(s));

// Method-Reference (identisch)
list.forEach(System.out::println);

// String::length statt s -> s.length()
list.stream().map(String::length);

Lesbarer in vielen Fällen, aber nicht überall sinnvoll, bei nicht-trivialen Lambdas lieber explizit.

Functional Composition

Funktionen lassen sich verketten:

Function<Integer, Integer> verdoppeln = x -> x * 2;
Function<Integer, Integer> plusZehn = x -> x + 10;

Function<Integer, Integer> kombi = verdoppeln.andThen(plusZehn);
kombi.apply(3);  // (3 * 2) + 10 = 16

Function<Integer, Integer> kombi2 = verdoppeln.compose(plusZehn);
kombi2.apply(3);  // (3 + 10) * 2 = 26

andThen: erst die eine, dann die andere. compose: umgekehrt (innere zuerst).

Klausur-Faustregeln

1. Lambda implementiert ein funktionales Interface. Genau EINE abstrakte Methode. Die wichtigsten für den Einstieg sind Function, Predicate, Consumer und Supplier aus java.util.function; weitere Spezialvarianten gibt es, brauchst du aber selten auswendig.

2. Lambda-Syntax kennen. Single-Param: keine Klammern (x -> x*2). Single-Expression: kein return, kein Block ((a,b) -> a+b).

3. Higher-Order-Methoden machen mehr mit weniger Code. map/filter/reduce sind funktionale Standard-Operationen.

4. Method-Reference :: ist Syntax-Sugar für ein Lambda das eine bestehende Methode aufruft. System.out::println statt s -> System.out.println(s).

5. Lambdas sind nicht Threads. Ein Lambda läuft NICHT automatisch parallel, es ist nur eine Funktion. Für Parallelität: stream().parallel() oder ExecutorService.

Häufige Stolpersteine

1. return vergessen bei mehrzeiligem Body. (a, b) -> { a + b } ist Compile-Fehler, entweder (a, b) -> a + b oder (a, b) -> { return a + b; }.

2. Effectively-final-Regel. Lambda kann nur lokale Variablen lesen, die effectively final sind (nicht verändert nach der Zuweisung). Innerhalb des Lambdas reassign → Compile-Error.

int x = 10;
Function<Integer, Integer> f = n -> n + x;  // ok
x = 20;  // ← x ist nicht mehr effectively final → Compile-Error oben

3. Stream nicht 2x nutzen. Ein Stream ist EINMAL nutzbar. stream.filter(...).count() und dann nochmal stream.forEach(...) → IllegalStateException.

4. null in Streams. null kann durch filter/map laufen und Probleme später machen. Vorher .filter(Objects::nonNull) als Schutz.

5. Lambda mit Seiteneffekt in map/filter. map und filter sollten pur sein, also nebenwirkungsfrei (= keinen externen State ändern, nur aus den Eingaben ein Ergebnis zurückgeben). Wenn du Seiteneffekte willst → forEach am Ende.

Klausur-Beispielaufgabe

Beispiel (typischer Klausur-Stil, zur Übung konstruiert).

Gegeben sei folgender Code:

List<Integer> werte = List.of(1, 2, 3, 4, 5, 6);

int ergebnis = werte.stream()
    .filter(n -> n % 2 == 1)
    .map(n -> n * n)
    .reduce(0, Integer::sum);

(a) Welchen Wert hat ergebnis? Gib die Zwischen-Liste nach jeder Operation an. (b) Welches funktionale Interface erwartet filter, welches map? (c) Das folgende Lambda kompiliert nicht, korrigiere es: (a, b) -> { a + b }. (d) Schreibe werte.forEach(x -> System.out.println(x)) mit einer Method-Reference.

Lösung:

  • (a) Schrittweise (siehe Abschnitt Higher-Order-Methoden): filter (ungerade) → [1, 3, 5], map (quadrieren) → [1, 9, 25], reduce mit Summe → 1 + 9 + 25 = 35. Also ergebnis = 35.
  • (b) filter erwartet ein Predicate<Integer> (T → boolean), map eine Function<Integer, Integer> (T → R). Beides steht in der Signatur-Karte im Abschnitt Funktionale Interfaces.
  • (c) Der Block-Body { ... } braucht ein return (siehe Lambda-Syntax und Stolperstein 1). Korrekt: (a, b) -> a + b (Single-Expression, return implizit) oder (a, b) -> { return a + b; }.
  • (d) werte.forEach(System.out::println) (siehe Method-References). Funktional identisch zum Lambda.
Teil 2·Visualisierung / Interaktiv

Interaktiv verstehen

Higher-Order-Pipeline

Der Visualizer startet mit der festen Liste [1, 2, 3, 4, 5, 6]. Du aktivierst oder deaktivierst per Checkbox drei Operationen und siehst nach jeder Stufe die Zwischen-Liste und am Ende das Endergebnis:

  • filter, wahlweise gerade oder ungerade behalten
  • map, wahlweise verdoppeln (n * 2) oder quadrieren (n * n)
  • reduce, wahlweise Summe oder Produkt

Schalte Operationen einzeln dazu und beobachte, wie sich die Liste nach jeder Stufe verändert. So entwickelst du Intuition für filter, map, reduce und ihre Zusammenarbeit, genau das brauchst du für Stream-Auswertungs-Aufgaben.

Interaktive Visualisierung

Interaktive Komponente: probiere sie im Topic-Player oben aus.

Klausur-Tipp: Bei "Was gibt der folgende Stream zurück?", geh Operation für Operation durch. filter wirft raus, map verändert, reduce kombiniert. Notiere dir nach jeder Operation die Zwischen-Liste aufs Papier, nie nur im Kopf zusammenfassen, das ist die häufigste Fehlerquelle in Stream-Aufgaben.


Code-Stepper: Schritt für Schritt

Drei Demos zum Durchklicken. Klick Step oder Auto und beobachte, wie sich Variablen, Zwischen-Listen und Konsole pro Schritt verändern. Die Annotation unten am Code erklärt, was in diesem Step passiert.

1. Stream-Kette: filter, map, reduce auswerten

Die Klausur-Standardaufgabe: eine Stream-Kette von Hand auswerten. Hier laufen [1..6] durch filter (gerade), map (quadrieren) und reduce (Summe). Achte darauf, wie die Liste nach jeder Operation schrumpft oder sich verwandelt.

Interaktive Visualisierung

Interaktive Komponente: probiere sie im Topic-Player oben aus.

2. Funktionales Interface: Predicate per Lambda

Ein Lambda ist nichts Magisches, es ist eine Instanz eines funktionalen Interfaces. Hier wird ein Predicate<Integer> durch ein Lambda gebaut und dann zweimal mit test(...) aufgerufen. Die abstrakte Methode test wird direkt durch den Lambda-Rumpf implementiert.

Interaktive Visualisierung

Interaktive Komponente: probiere sie im Topic-Player oben aus.

3. Effectively-final: warum das Reassign bricht

Ein Lambda darf lokale Variablen nur lesen, wenn sie effectively final sind. Sobald die Variable nach der Zuweisung noch verändert wird, ist sie es nicht mehr, und die Zeile mit dem Lambda wird zum Compile-Error. Hier siehst du, an welcher Stelle der Compiler abbricht.

Interaktive Visualisierung

Interaktive Komponente: probiere sie im Topic-Player oben aus.

Klausur-Faustregeln (Quick-Check)

Drei Faustregeln, die in Stream- und Lambda-Aufgaben fast immer Punkte bringen:

  1. Stream-Auswertung schrittweise: Bei jeder Stream-Kette jede Operation einzeln durchgehen (filter → map → reduce) und die Zwischen-Liste notieren. Nie im Kopf zusammenfassen, das ist die häufigste Fehlerquelle.
  2. Effectively-final-Regel: Ein Lambda kann lokale Variablen nur lesen, wenn sie nach der Zuweisung nicht mehr verändert werden. Ein Reassign der Variable macht die Lambda-Zeile zum Compile-Error.
  3. Method-Reference :: ist Syntax-Zucker: System.out::println und s -> System.out.println(s) sind funktional identisch und in der Klausur austauschbar. Lass dich von :: nicht verwirren, übersetze es im Kopf ins gleichwertige Lambda.
Teil 3·Quiz / Klausurfragen

Praxis-Übung

Lambdas & Higher-Order, Praxis-Übung

6 Aufgaben zu Lambda-Syntax, funktionalen Interfaces, Stream-Operationen.

Klausurfragen mit Lösungen (6)

F1.Welche Lambda-Syntax ist KORREKT für 'verdoppele x'?

Antwort: x -> x * 2

Erklärung: Java-Syntax ist `x -> x * 2`. JavaScript wäre `=>`, Python `lambda x:`, klassisches Java `function(x)`. Pfeil-Operator `->` ist Java-Spezifika.

F2.Was ist ein 'funktionales Interface'?

Antwort: Ein Interface mit genau einer abstrakten Methode

Erklärung: Funktionales Interface = genau EINE abstrakte Methode (default-Methoden zählen nicht). Beispiele: Function, Predicate, Consumer, Comparator. Nur funktionale Interfaces können von Lambdas implementiert werden.

F3.Ein Lambda läuft automatisch in einem eigenen Thread (parallel).

Antwort: Falsch

Erklärung: FALSCH. Lambdas sind reine Funktionen, kein Threading. Für Parallelität: `stream().parallel()` oder ExecutorService. Lambdas sind nur eine kompakte Schreibweise, Ausführung ist sequenziell wie normaler Code.

Typ: Wahr/Falsch

F4.Welche Higher-Order-Methode WIRFT Elemente RAUS, die ein Kriterium nicht erfüllen?

Antwort: filter

Erklärung: filter behält nur die Elemente, für die der Predicate true ist. map TRANSFORMIERT jedes (kein Element verschwindet), reduce KOMBINIERT zu einem Wert, forEach iteriert.

F5.Welche Aussagen über Lambdas sind RICHTIG?

Richtige Antworten: Lambdas können effectively-final lokale Variablen lesen; `x -> x * 2` ist valides Java; Method-References wie `String::length` sind eine Kurzschreibweise; Ein funktionales Interface hat 1 abstrakte Methode

Erklärung: Richtig: effectively-final Zugriff erlaubt, Lambda-Syntax mit ->, Method-References, 1 abstrakte Methode. Falsch: Lambdas können lokale Variablen NICHT reassign (Compile-Error); Streams sind EINMAL nutzbar (IllegalStateException sonst).

Typ: Multi-Select

F6.Ordne das funktionale Interface seiner Signatur zu:

Zuordnungen:

  • Function<T, R> → T → R (Transformation)
  • Predicate<T> → T → boolean (Filter-Bedingung)
  • Consumer<T> → T → void (Action mit Seiteneffekt)
  • Supplier<T> → () → T (Erzeuger ohne Input)

Erklärung: Die 4 Basis-Interfaces aus java.util.function. Function (transformiert), Predicate (testet), Consumer (verbraucht), Supplier (erzeugt).

Typ: Zuordnung

Teil 4·Quiz / Klausurfragen

Klausur-Quiz

Klausurfragen mit Lösungen (6)

F1.Was gibt aus: Stream.of(1,2,3,4).filter(n -> n%2==0).map(n -> n*10).reduce(0, Integer::sum)?

Antwort: 60

Erklärung: Schritt 1 filter: [2,4]. Schritt 2 map *10: [20, 40]. Schritt 3 reduce mit sum: 20+40=60. Klausur-Klassiker: jeden Schritt einzeln durchgehen.

F2.Welche Lambda-Syntax ist KORREKT für 2 Parameter?

Antwort: (a, b) -> a + b

Erklärung: Bei MEHREREN Parametern sind Klammern PFLICHT: `(a, b) -> a + b`. Bei einem Parameter sind Klammern optional: `x -> x*2` oder `(x) -> x*2`. `=>` ist JavaScript, nicht Java.

F3.System.out::println und s -> System.out.println(s) sind funktional identisch.

Antwort: Wahr

Erklärung: RICHTIG. Beides ist ein Consumer<String> mit gleicher Wirkung. `::` ist Method-Reference-Syntax, syntaktischer Zucker für das entsprechende Lambda.

Typ: Wahr/Falsch

F4.Im Code: int x = 5; List<Integer> l = List.of(1,2,3); l.stream().forEach(n -> x = x + n);

Antwort: Compile-Fehler: x ist nicht effectively final

Erklärung: Compile-Fehler! Lambdas dürfen lokale Variablen nur LESEN, wenn sie effectively final sind. `x = x + n` ist eine Reassignment → Bricht das. Lösung: int[]{5} (Array bleibt final, Inhalt wandelt) oder besser: reduce verwenden.

F5.Ein Lambda implementiert ein {{1}} Interface mit genau einer abstrakten Methode. filter braucht ein {{2}}, map braucht eine {{3}}. Die Stream-Methode, die alle Elemente zu einem Wert kombiniert, heißt {{4}}.

Lösungen pro Lücke:

  • {{1}}: funktionales / functional
  • {{2}}: Predicate
  • {{3}}: Function
  • {{4}}: reduce

Erklärung: Standard-Vokabular Lambdas + Streams. Funktionales Interface, Predicate (filter), Function (map), reduce (Aggregation). In einer deutschsprachigen Klausur ist "funktionales" die bevorzugte Schreibweise; das englische "functional" akzeptieren wir hier nur als Synonym.

Typ: Lückentext

F6.Welche Stream-Pipeline gibt eine Liste der quadrierten geraden Zahlen aus [1..10] zurück?

Richtige Reihenfolge:

  1. Stream.of(1,2,3,4,5,6,7,8,9,10)
  2. .filter(n -> n % 2 == 0)
  3. .map(n -> n * n)
  4. .collect(Collectors.toList())

Erklärung: Erst Quelle, dann filter (gerade), dann map (quadrieren), dann sammeln. Reihenfolge wichtig: filter VOR map ist effizienter (weniger Elemente zu transformieren). Endresultat: [4, 16, 36, 64, 100].

Typ: Reihenfolge

Zur KategorieProgrammiergrundlagen.Mehr Themen entdeckenZum Themen-Hub.

UniProMax ist eine themenbasierte Lernplattform für Studierende an deutschen Unis.

Wir glauben, dass Verstehen besser ist als Auswendiglernen. Wir bauen Lerneinheiten die zeigen statt erzählen. Code, Visualisierung, Quiz. Auf Deutsch.

Marke

UniProMaxUniProMax

Themenbasiert, visuell, interaktiv.

Inhalte

  • Alle Themen (Hub)
  • Programmiergrundlagen
  • Algorithmen
  • Mathematik
  • Statistik
  • Datenbanken
  • Rechnungswesen
  • VWL

Studiengang-Filter

  • Informatik
  • Wirtschaftsinformatik
  • BWL
  • Data Science
  • VWL
  • Wirtschaftsingenieurwesen
  • Mathe
  • Psychologie
  • weitere Studiengänge folgen

Plattform

  • Mein Fortschritt
  • Impressum
  • Datenschutz
© 2026 UniProMaxAlle Systeme onlinev0.2 / Sommersemester 2026
UniProMaxUniProMaxUniProMaxUniProMax