ProgrammierungKotlin Entwickler

Wie werden in Kotlin Higher-Order-Funktionen und Lambda-Ausdrücke implementiert? Beschreiben Sie die Nuancen der Übergabe und Rückgabe von Funktionen, die Besonderheiten der Syntax, die grundlegenden Einschränkungen und geben Sie Codebeispiele an.

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

Antwort.

Higher-Order-Funktionen sind Funktionen, die andere Funktionen als Parameter akzeptieren oder diese zurückgeben. Kotlin verwendet Lambda-Ausdrücke, um Verhalten komfortabel als Wert zu übertragen.

Beispiel für die Deklaration:

fun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int { return operation(a, b) } val sum = operateOnNumbers(3, 2) { x, y -> x + y } // sum = 5

Übergabe von Funktionen:

  • Funktionen können nicht nur als Lambda-Ausdruck, sondern auch per Referenz übergeben werden:
fun multiply(x: Int, y: Int) = x * y operateOnNumbers(2, 3, ::multiply)

Rückgabe von Funktionen:

fun makeMultiplier(factor: Int): (Int) -> Int = { x -> x * factor } val triple = makeMultiplier(3) val result = triple(10) // 30

Besonderheiten:

  • Ein Lambda-Ausdruck kann nicht mehr als einen nicht benannten Parameter (it) haben.
  • An eine Funktion können benannte Parameter übergeben werden, aber die Typen müssen explizit angegeben werden, wenn der Typ nicht abgeleitet werden kann.
  • Lambda-Ausdrücke sind Objekte (anonyme Klassen), was die Leistung bei einer großen Anzahl von Aufrufen in heißen Schleifen beeinflusst (dies wird durch inline-Funktionen gelöst).
  • Für Lambdas mit erfassenden Variablen wird ein Closure verwendet.

Fangfrage.

Was ist der Unterschied zwischen der Deklaration des Funktionstyps (Int, Int) -> Int und der Verwendung des Typs Function2<Int, Int, Int>?

Antwort: Die Syntax (Int, Int) -> Int ist einfach eine "schönere" Deklaration (syntaktischer Zucker) für das Interface Function2<Int, Int, Int>. In der Praxis sind beide Varianten vollständig austauschbar.

val f1: (Int, Int) -> Int = { x, y -> x + y } val f2: Function2<Int, Int, Int> = { x, y -> x + y }

Aber die erste Variante wird normalerweise wegen der Lesbarkeit bevorzugt.

Beispiele für reale Fehler aufgrund mangelnden Wissens über die Feinheiten des Themas.


Geschichte

In einem großen Ereignisverarbeitungssystem wurden Dutzende von Lambda-Ausdrücken innerhalb einer Schleife ohne Verwendung von Inline-Funktionen erstellt. Dies verursachte eine hohe Belastung für den GC und eine Leistungseinbuße, da für jeden Aufruf ein separates anonymes Funktionsobjekt erstellt wurde.


Geschichte

Beim Versuch, eine Funktion aus einer anderen Funktion zurückzugeben, wurde die Signatur der zurückgegebenen Funktion nicht korrekt angegeben, was zu einem Kompilierungsfehler und einer langen Fehlersuche führte. Der Fehler lag im Fehlen der Klammern im Typ: fun foo(): Int -> Int anstelle von fun foo(): (Int) -> Int.


Geschichte

Ein Entwickler versuchte, ein Lambda ohne explizite Typangabe als Parameter einer anderen Funktion mit nicht abgeleitetem Typ zu verwenden, was zu dem Fehler "cannot infer a type for this parameter" führte. Das Problem wurde durch die explizite Angabe des Typs des Lambdas oder des Parameters der Funktion gelöst.