Higher-order function: cos'è, caratteristiche ed esempi in Kotlin

Autore: IT Sectr Pubblicato: 2026-06-23 Tempo di lettura: 8 min

Una higher-order function è una funzione che accetta un'altra funzione come parametro o restituisce una funzione come risultato. In Kotlin, le funzioni sono cittadini di prima classe: possono essere memorizzate in variabili, passate come argomenti e restituite. Secondo la documentazione di Kotlin (2026), le higher-order functions riducono la duplicazione del codice in media del 30% rispetto agli approcci imperativi. Higher-order function è un concetto fondamentale dello stile funzionale nello sviluppo moderno.

Punti chiave

  • Higher-order function — funzione che prende un'altra funzione come parametro o ne restituisce una
  • Tipo funzionale (T) -> R — la base per dichiarare parametri di funzione in una firma
  • Espressione lambda — modo compatto per passare una funzione a una higher-order function
  • Passaggio di comportamento consente di parametrizzare operazioni senza ereditarietà e interfacce
  • Restituzione di funzione da una higher-order function viene utilizzata per fabbriche e chiusure

Cos'è una higher-order function?

Una higher-order function è una funzione che possiede almeno una di due caratteristiche: prendere un'altra funzione come argomento o restituire una funzione come risultato. In matematica, queste funzioni sono chiamate funzionali o operatori. Nella programmazione, sono apparse in Lisp nel 1958 e sono diventate parte obbligatoria di tutti i linguaggi moderni — JavaScript, Python, Swift, Kotlin, Scala e Haskell. Una higher-order function consente di astrarre da un'operazione specifica e passare il comportamento come valore.

La differenza chiave tra una higher-order function e una funzione normale è la presenza di un parametro con tipo funzionale o di un valore funzionale restituito. In Kotlin, un tipo funzionale si scrive come (ParamType) -> ReturnType. Ad esempio, il tipo (Int) -> String significa una funzione che prende Int e restituisce String. Il tipo () -> Unit denota una funzione senza parametri che non restituisce alcun valore utile. È questo sistema di tipi che rende le higher-order functions type-safe.

L'opposto di una higher-order function è una first-class function (funzione di prima classe). First-class function significa che una funzione può essere usata come qualsiasi altro valore: assegnata a una variabile, memorizzata in una collezione, passata come argomento. Una higher-order function è una funzione che usa first-class functions per accettare o restituire. Kotlin supporta entrambi i concetti a livello di linguaggio senza librerie aggiuntive.

Come funziona una higher-order function in Kotlin?

In Kotlin, qualsiasi funzione che abbia almeno un parametro dichiarato con un tipo funzionale o il cui tipo di ritorno è funzionale viene automaticamente considerata una higher-order function. Il compilatore non richiede un'annotazione speciale — basta specificare (T) -> R nella firma. Quando si chiama una tale funzione, l'argomento è un'espressione lambda, un riferimento a una funzione esistente tramite ::, o un valore funzionale memorizzato in una variabile.

kotlin
fun operate(a: Int, b: Int, op: (Int, Int) -> Int): Int {
    return op(a, b)
}

fun main() {
    val sum = operate(10, 20) { x, y -> x + y }
    println(sum) // 30
}

Nel listato, la funzione operate prende due interi e un parametro op di tipo (Int, Int) -> Int. Il corpo della funzione chiama l'operazione passata in una singola riga. In main, la chiamata a operate passa la lambda { x, y -> x + y } — Kotlin la posiziona dopo le parentesi grazie alla sintassi trailing lambda. Se la lambda fosse l'ultimo argomento, potrebbe essere spostata completamente fuori dalle parentesi, migliorando la leggibilità delle catene di chiamate.

Sotto il cofano, Kotlin compila le higher-order functions attraverso l'interfaccia Function (Function2 per due parametri). Ogni lambda si trasforma in una classe anonima che implementa la corrispondente interfaccia FunctionN. Ciò significa che la creazione di una lambda comporta l'allocazione di un oggetto per ogni operazione. Per ridurre l'overhead, Kotlin supporta le funzioni inline, che sostituiscono il corpo della higher-order function nel sito di chiamata, eliminando la creazione della classe anonima.

Funzioni come parametri: passaggio di comportamento

Passare una funzione come argomento è il pattern più comune di utilizzo delle higher-order functions. Invece di creare una gerarchia di classi con un metodo polimorfico, lo sviluppatore passa il comportamento desiderato direttamente al punto di utilizzo. Ciò implementa il principio Aperto/Chiuso senza ereditarietà: una nuova operazione viene aggiunta come nuova lambda, non come nuova sottoclasse.

Per le funzioni nullable, Kotlin usa il tipo ((T) -> R)? con un punto interrogativo dopo le parentesi. Tale funzione può essere chiamata solo dopo un controllo null o tramite l'operatore ?.invoke(). In questo caso, la firma della higher-order function indica esplicitamente che passare una funzione è opzionale — il codice chiamante può omettere l'argomento. Ciò è utile per callback e gestori di eventi con comportamento opzionale.

kotlin
fun <T> List<T>.customFilter(
    predicate: (T) -> Boolean
): List<T> {
    val result = mutableListOf<T>()
    for (item in this) {
        if (predicate(item)) result.add(item)
    }
    return result
}

fun main() {
    val numbers = listOf(1, 2, 3, 4, 5)
    val even = numbers.customFilter { it % 2 == 0 }
    println(even) // [2, 4]
}

La funzione di estensione customFilter prende un parametro predicate di tipo funzionale (T) -> Boolean. All'interno del ciclo, chiama predicate per ogni elemento e raccoglie quelli corrispondenti. La chiamata in main passa la lambda { it % 2 == 0 }, dove it è il nome implicito dell'unico parametro della lambda. Grazie alle higher-order functions, la logica di filtraggio è completamente isolata dal meccanismo di attraversamento della collezione.

Funzioni come valori di ritorno

Una higher-order function può restituire una funzione — questo pattern è chiamato fabbrica di funzioni o generatore di comportamento. La funzione restituita può catturare (closure) variabili dall'ambito esterno, preservandole tra le chiamate. Ciò consente di creare gestori configurabili e operazioni specializzate basate su modelli comuni.

Quando restituisce una funzione, Kotlin deduce il tipo funzionale ((T) -> R)? restituito dalla firma. Il compilatore verifica che tutte le espressioni return nel corpo restituiscano valori funzionali compatibili. Le variabili catturate vengono memorizzate nell'oggetto lambda e rimangono accessibili finché esiste un riferimento ad esso. Questo è un meccanismo potente, ma richiede attenzione alla gestione della memoria.

kotlin
fun makeMultiplier(factor: Int): (Int) -> Int {
    return { x -> x * factor }
}

fun main() {
    val double = makeMultiplier(2)
    val triple = makeMultiplier(3)
    println(double(5))  // 10
    println(triple(5)) // 15
}

La funzione makeMultiplier prende un fattore e restituisce una lambda { x -> x * factor }, dove factor viene catturato dall'ambito esterno (closure). Ogni chiamata a makeMultiplier crea una nuova funzione con il proprio valore di factor. Le variabili double e triple memorizzano le funzioni restituite e possono essere chiamate più volte. Questo pattern è ampiamente utilizzato nella configurazione di client HTTP, decoratori e middleware.

Higher-order function con lambda: esempi

La combinazione di higher-order functions e lambda consente di costruire catene di operazioni espressive senza variabili intermedie. La libreria standard di Kotlin contiene decine di higher-order functions: let, run, apply, also, filter, map, flatMap, fold, reduce, forEach, groupBy e altre. Ognuna prende una lambda ed esegue una trasformazione dei dati con essa.

kotlin
data class User(val name: String, val age: Int)

fun main() {
    val users = listOf(
        User("Alice", 25),
        User("Bob", 17),
        User("Charlie", 30)
    )

    val result = users
        .filter { it.age >= 18 }
        .map { it.name.uppercase() }
        .sorted()
    println(result) // [ALICE, CHARLIE]
}

Nell'esempio, una catena di tre higher-order functions elabora una lista di utenti. filter prende un predicato, mantenendo solo i maggiorenni. map trasforma ogni utente in un nome in maiuscolo. sorted ordina il risultato in ordine crescente. Ogni operazione prende una lambda, e Kotlin garantisce la sicurezza dei tipi in ogni fase. Senza higher-order functions, si dovrebbe scrivere un ciclo con if, liste temporanee e ordinamento manuale.

Domande frequenti

In cosa una higher-order function differisce da una funzione normale?

Una higher-order function prende un'altra funzione come parametro o ne restituisce una. Una funzione normale lavora solo con dati — numeri, stringhe, oggetti. Una higher-order function lavora con il comportamento, passando la logica come argomento.

Una higher-order function può essere inline?

Sì, il modificatore inline elimina l'overhead di creazione di una classe anonima per una lambda. Usando crossinline o noinline, è possibile controllare quali lambda vengono sostituite e quali rimangono come oggetti.

Qual è il tipo funzionale di una funzione senza parametri?

Una funzione senza parametri e senza valore di ritorno ha il tipo () -> Unit. Una funzione con un parametro di tipo T e ritorno R si scrive come (T) -> R. Per due parametri — (T, U) -> R, e così via fino a 22 argomenti.

Qual è la differenza tra una lambda e una funzione anonima?

Una lambda è una notazione concisa { args -> body }, una funzione anonima è fun(args): ReturnType { body }. Una lambda non può avere return senza etichetta, una funzione anonima sì. Entrambe possono essere passate a una higher-order function.

Quando evitare le higher-order functions?

Evitare higher-order functions nei cicli critici senza inline — ogni lambda crea un oggetto. Per codice critico per le prestazioni, utilizzare inline fun. Inoltre, non abusare di lambda profondamente annidate — ciò riduce la leggibilità.

Riepilogo

  • Higher-order function — funzione che prende un'altra funzione come parametro o ne restituisce una; il fondamento della programmazione funzionale in Kotlin
  • Tipo funzionale (T) -> R dichiara un parametro di funzione con firma type-safe
  • Espressione lambda — sintassi compatta { args -> body } per passare comportamento
  • Funzioni inline eliminano l'overhead di creazione di classi anonime per le lambda
  • Fabbriche di funzioni restituiscono una funzione con contesto catturato tramite il meccanismo di chiusura
  • Catene di operazioni filter-map-sorted sono costruite su higher-order functions senza variabili intermedie
  • Sicurezza dei tipi delle higher-order functions è garantita dal sistema di tipi funzionali di Kotlin al momento della compilazione

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche