ProgrammazioneSviluppatore Kotlin

Come vengono implementate le funzioni di ordine superiore e le espressioni lambda in Kotlin? Descrivi le sfumature del passaggio e del ritorno delle funzioni, le caratteristiche della sintassi, le principali limitazioni e fornisci esempi di codice.

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Funzioni di ordine superiore — sono funzioni che accettano altre funzioni come parametri o le restituiscono. Kotlin utilizza espressioni lambda per passare comportamenti come valori in modo conveniente.

Esempio di dichiarazione:

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

Passaggio delle funzioni:

  • Le funzioni possono essere passate non solo come espressioni lambda, ma anche come riferimento: :
fun multiply(x: Int, y: Int) = x * y operateOnNumbers(2, 3, ::multiply)

Ritorno di una funzione:

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

Caratteristiche:

  • Una lambda può avere al massimo un parametro senza nome (it).
  • È possibile passare parametri named a una funzione, ma i tipi devono essere specificati esplicitamente se non riesce a dedurre il tipo.
  • Le espressioni lambda sono oggetti (classi anonime), il che influisce sulle prestazioni quando ci sono molti call in loop caldi (si risolve con funzioni inline).
  • Per le lambda che catturano variabili viene utilizzato il closure (chiusura).

Domanda insidiosa.

Qual è la differenza tra la dichiarazione del tipo di funzione (Int, Int) -> Int e l'uso del tipo Function2<Int, Int, Int>?

Risposta: La sintassi (Int, Int) -> Int è semplicemente una dichiarazione "più bella" (sintassi zuccherosa) per l'interfaccia Function2<Int, Int, Int>. Nella pratica, entrambe le opzioni sono completamente intercambiabili.

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

Tuttavia, la prima opzione è solitamente preferibile per la leggibilità.

Esempi di errori reali a causa della mancanza di conoscenza delle sfumature dell'argomento.


Storia

In un grande sistema di elaborazione eventi, sono state create decine di espressioni lambda all'interno di un ciclo senza utilizzare funzioni inline. Ciò ha causato un alto carico sul GC e una degradazione delle prestazioni, poiché per ogni invocazione veniva creato un oggetto anonimo di funzione separato.


Storia

Nel tentativo di restituire una funzione da un'altra funzione, la firma della funzione restituita non è stata specificata correttamente, portando a un errore di compilazione e a una lunga ricerca della causa. L'errore era nella mancanza di parentesi nel tipo: fun foo(): Int -> Int invece del corretto fun foo(): (Int) -> Int.


Storia

Uno sviluppatore ha tentato di utilizzare una lambda senza un tipo esplicitamente specificato come parametro di un'altra funzione con un tipo non dedotto esplicitamente, il che ha portato all'errore "cannot infer a type for this parameter". Il problema è stato risolto specificando esplicitamente il tipo della lambda o del parametro della funzione.