ProgrammierungMiddle Kotlin Entwickler

Was sind Scope-Funktionen in Kotlin (let, also, run, apply, with)? Was sind die Unterschiede zwischen diesen Funktionen, wie wählt man sie für verschiedene Aufgaben aus, welche Feinheiten können bei ihrer Verwendung auftreten? Geben Sie Beispiele an.

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

Scope-Funktionen ("Bereichsfunktionen") in Kotlin sind Standardfunktionen (let, also, run, apply, with), die es ermöglichen, den Kontext der Ausführung eines Codeblocks für ein Objekt zu steuern. Sie unterscheiden sich:

  • im Rückgabewert,
  • darin, wie das Objekt innerhalb des Blocks verfügbar ist: über it oder über this.

Kurze Zusammenfassung:

Funktionthis/itGibt zurückWofür?
letitErgebnisVerkettung von Operationen, Arbeit mit Nullable, Map
alsoitObjektSide-Effects, Logging, Debugging
runthisErgebnisBerechnungen, Initialisierung mit Rückgabe
applythisObjektObjektkonfiguration, Builder
withthisErgebnisArbeit mit externen APIs, Objekt "außen"

Beispiele:

  • let: praktisch, wenn das Objekt nullable ist:
val str: String? = "Text" str?.let { println(it.length) }
  • apply: Objektkonfiguration:
val paint = Paint().apply { color = Color.RED strokeWidth = 2f }
  • run: Ausführung auf dem Objekt, Rückgabe des Ergebnisses:
val length = "abcde".run { length }
  • with: für die Arbeit mit einem externen Objekt:
val sb = StringBuilder() with(sb) { append("Hallo, ") append("Welt!") toString() }
  • also: für Side-Effects (z.B. Logs):
val list = mutableListOf(1, 2, 3) list.also { println("Vorher: $it") }.add(4)

Worauf man achten sollte:

  • let erstellt eine Kopie des Objekts in it, es ist nicht sehr praktisch, Eigenschaften des Objekts zu ändern.
  • apply und also geben immer das ursprüngliche Objekt zurück (this / it), nützlich für Builder.
  • run/with werden oft verwechselt: with ist eine normale Funktion, keine Extension.

Fangfrage.

Was ist der Unterschied zwischen let und also?

Antwort:

  • Beide verwenden it innerhalb des Blocks,
  • let gibt das Ergebnis des Lambdas zurück, wird häufig für Transformationsverkettungen verwendet,
  • also gibt das ursprüngliche Objekt zurück, wird für Nebeneffekte (Logging, Debugging) verwendet, um nicht in die Transformationskette einzugreifen.

Beispiel:

val result = listOf(1).also { println(it) }.map { it * 2 } // Ergebnis — List<Int>

Beispiele für echte Fehler aufgrund von Unkenntnis der Feinheiten des Themas:


Geschichte

Ein Neuling verwendete let, um ein Objekt zu konfigurieren, und dachte, dass er seinen Zustand "in der Kette" ändern könne. Am Ende des Konfigurationsblocks erhielt er nicht das Objekt, sondern das Ergebnis des Lambdas (z.B. nichts), was die Verkettenstruktur des DSL störte.


Geschichte

Bei der Programmierung mit Nullable-Objekten verwendeten wir run anstelle von let, wobei wir den Unterschied im Rückgabewert nicht bemerkten. Infolgedessen unterschied sich das Ergebnis des Ausdrucks von dem, was erwartet wurde, null tauchte dort auf, wo es nicht sein sollte — die Logik der Anwendung brach zusammen.


Geschichte

In einem großen Builder verwendeten wir versehentlich with für interne Objekte, in der Annahme, dass es sich um das Extension-Muster handelte. Da with keine Extension-Funktion ist, funktionierte die Kette aus mehreren with-Blöcken nicht korrekt, interne Aufrufe vermischten sich und gingen über den aktuellen Objektkontext hinaus. Wir mussten die Hierarchie zur Objekterstellung vollständig neu schreiben.