break und continue in Kotlin forEach: Wie es unter der Haube funktioniert

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Warum break und continue in Kotlin forEach nicht funktionieren (und wie man es behebt)

\n\n

Wenn Sie jemals in Kotlin programmiert und versucht haben, eine forEach-Schleife vorzeitig mit break zu verlassen, sind Sie höchstwahrscheinlich auf eine rote Unterstreichung in der IDE und einen Kompilierungsfehler gestoßen. Kommt Ihnen bekannt vor? Sie sind nicht allein – dies ist eine der häufigsten Fragen zu Kotlin auf Stack Overflow mit tausenden von Aufrufen. In diesem Artikel zeigen wir nicht nur, wie Sie die Einschränkung umgehen, sondern werfen auch einen Blick unter die Haube: Warum wurde es so gemacht, wie funktioniert return innerhalb einer Lambda und welche Alternativen gibt es? Los geht's!

\n\n

Klassisches Beispiel: Anfängerfehler

\n\n

Stellen Sie sich vor, Sie haben eine Liste von Zahlen und möchten nur die ersten drei Elemente auf dem Bildschirm ausgeben. Intuitiv möchte man so schreiben:

\n\n
val numbers = listOf(1, 2, 3, 4, 5)\nnumbers.forEach {\n    println(it)\n    if (it == 3) break // Kompilierungsfehler!\n}\n
\n\n

Der Compiler gibt einen Fehler aus: ‘break’ and ‘continue’ are only allowed inside a loop. Warum? Schließlich ist forEach doch eine Art Schleife? Die Antwort liegt darin, wie forEach tatsächlich aufgebaut ist.

\n\n

Anatomie von forEach: Es ist keine Schleife, sondern ein Funktionsaufruf

\n\n

Öffnen wir die Standardbibliothek von Kotlin und betrachten die Signatur von forEach:

\n\n
public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {\n    for (element in this) action(element)\n}\n
\n\n

Das Schlüsselwort hier ist inline. Das bedeutet, dass der Funktionskörper während der Kompilierung direkt an die Aufrufstelle eingefügt wird. Trotzdem ist forEach eine Funktion höherer Ordnung, die eine andere Funktion (Lambda) als Argument akzeptiert. Eine Lambda ist jedoch kein Codeblock einer Schleife, sondern ein gewöhnliches Funktionsobjekt. break und continue sind nur innerhalb von for-, while- oder do-while-Konstrukten sinnvoll, nicht innerhalb einer beliebigen Funktion.

\n\n

Mit anderen Worten: Wenn Sie numbers.forEach { ... } schreiben, erzeugen Sie keine Schleife – Sie rufen eine Methode auf, die selbst intern eine for-Schleife verwendet. Der Compiler kann break nicht aus Ihrer Lambda in die äußere Schleife „durchreichen“, da die Lambda eine separate Entität ist. Das ist kein Fehler, sondern Design.

\n\n

Wie return in forEach funktioniert (überraschende Wendung)

\n\n

return innerhalb von forEach funktioniert jedoch ... aber nicht so, wie Sie erwarten. Schauen Sie:

\n\n
fun main() {\n    val numbers = listOf(1, 2, 3, 4, 5)\n    numbers.forEach {\n        if (it == 3) return // Beendet main()!\n        println(it)\n    }\n    println("Diese Zeile wird nicht ausgeführt")\n}\n
\n\n

Die Ausgabe ist: 1 2. return ist hier ein non-local return (nicht-lokale Rückkehr). Es beendet nicht nur die Lambda, sondern die gesamte äußere Funktion (main). Dies funktioniert, weil forEach als inline deklariert ist. Der Compiler fügt den Lambda-Code direkt in den Körper von main ein, und return bezieht sich dann auf die nächstgelegene deklarierte fun, also main.

\n\n

Wenn Sie nur aus forEach aussteigen möchten, nicht aus der gesamten Funktion, verwenden Sie ein Label (Markierung):

\n\n
fun main() {\n    val numbers = listOf(1, 2, 3, 4, 5)\n    numbers.forEach loop@{\n        if (it == 3) return@loop\n        println(it)\n    }\n    println("Diese Zeile wird ausgeführt")\n}\n
\n\n

Ausgabe: 1 2 und Diese Zeile wird ausgeführt. Das Label loop@ sagt dem Compiler: „Verlasse die Lambda, aber nicht main“. Im Grunde ist dies ein Analogon zu continue – wir überspringen die aktuelle Iteration und gehen zum nächsten Element über.

\n\n

Wie man break und continue in forEach emuliert

Blogs

Buchempfehlungen