Scope functions — cosa sono, tipi e applicazione in Kotlin

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

Le scope functions in Kotlin sono cinque funzioni della libreria standard (let, run, with, apply, also) che eseguono un blocco di codice nel contesto di un oggetto. Differiscono nel modo di accedere all'oggetto (tramite it o this) e nel valore restituito (oggetto di contesto o risultato della lambda). Secondo la documentazione Kotlin (2026), la scelta corretta della scope function riduce il codice boilerplate del 25–40%. Le scope functions aiutano a scrivere inizializzazione, configurazione e trasformazioni di oggetti in uno stile dichiarativo.

Punti chiave

  • Scope functions — cinque funzioni Kotlin (let, run, with, apply, also) per lavorare nel contesto di un oggetto
  • Contesto tramite this — run, with, apply usano this, consentendo di chiamare direttamente i metodi dell'oggetto
  • Contesto tramite it — let e also passano l'oggetto come parametro it con nome esplicito
  • Restituzione dell'oggetto — apply e also restituiscono l'oggetto di contesto per concatenare chiamate
  • Restituzione del risultato — let, run e with restituiscono l'ultima espressione della lambda

Cosa sono le scope functions?

Le scope functions sono un insieme di cinque funzioni (let, run, with, apply, also) integrate nella libreria standard di Kotlin. Ognuna di esse prende una lambda e modifica temporaneamente l'ambito in modo che il codice all'interno della lambda venga eseguito nel contesto di un oggetto specifico. Questo elimina la ripetizione del nome dell'oggetto per chiamate multiple e raggruppa le operazioni correlate in un unico blocco.

Due caratteristiche chiave distinguono le scope functions: il modo di accedere all'oggetto di contesto (tramite this o tramite it) e il valore restituito (l'oggetto di contesto stesso o il risultato della lambda). L'accesso tramite this rende l'oggetto un ricevitore implicito — metodi e proprietà vengono chiamati senza prefisso. L'accesso tramite it passa l'oggetto come parametro esplicito della lambda. Restituire l'oggetto consente il concatenamento, restituire il risultato consente di assegnare il valore trasformato.

Storicamente, le scope functions sono emerse come sostituzione del classico pattern Builder e delle variabili temporanee per l'inizializzazione. Invece di creare una classe builder separata, lo sviluppatore scrive apply { proprietà1 = ...; proprietà2 = ... }. Questo riduce la quantità di codice e rende l'inizializzazione più leggibile. Nell'ecosistema Kotlin, le scope functions sono utilizzate in tutti i framework e le librerie come idioma linguistico standard.

Confronto delle scope functions: this vs it, restituzione oggetto vs risultato

La scelta della scope function corretta è determinata da due domande: se si desidera utilizzare this o it per accedere all'oggetto, e se si desidera restituire l'oggetto stesso o il risultato della lambda. La combinazione di due caratteristiche binarie dà quattro possibili opzioni, e with occupa un posto speciale come unica funzione che non è un'estensione.

FunzioneAccessoRestituzioneEstensione?Scenario tipico
letitrisultato lambdaTrasformazione, controllo null
runthisrisultato lambdaCalcolo con contesto
withthisrisultato lambdanoRaggruppamento chiamate
applythisoggetto di contestoInizializzazione proprietà
alsoitoggetto di contestoEffetti collaterali, logging

Per configurare un oggetto senza restituire un nuovo valore, utilizzare apply (accesso tramite this) o also (accesso tramite it). Per calcolare un nuovo valore basato su un oggetto, utilizzare let (accesso tramite it) o run (accesso tramite this). Per raggruppare chiamate senza concatenamento, utilizzare with. Seguire questo schema rende il codice prevedibile per altri sviluppatori familiari con Kotlin.

apply e also: configurazione con restituzione oggetto

apply è una scope function che restituisce l'oggetto di contesto e fornisce l'accesso ad esso tramite this. È lo strumento ideale per inizializzare le proprietà di un oggetto dopo la sua creazione. All'interno del blocco apply, le proprietà possono essere impostate senza ripetere il nome dell'oggetto. Il metodo restituisce l'oggetto stesso, consentendo di incorporare apply in catene o utilizzarlo negli inizializzatori.

kotlin
data class ServerConfig(
    var host: String = "localhost",
    var port: Int = 8080,
    var useTls: Boolean = false
)

val config = ServerConfig().apply {
    host = "api.example.com"
    port = 443
    useTls = true
}

Nell'esempio, apply configura ServerConfig accedendo direttamente alle proprietà tramite this (this è omesso). Senza apply, si dovrebbe scrivere config.host = ..., config.port = ... — tre riferimenti ripetitivi alla stessa variabile. La restituzione dell'oggetto consente di salvare il risultato della configurazione in val config. also, a differenza di apply, passa l'oggetto tramite it ed è adatto per effetti collaterali.

kotlin
fun saveUser(user: User) {
    validate(user).also { result ->
        println("Risultato della validazione: $result")
    }
    val savedUser = user.also {
        log("Saving user ${it.id}")
        database.save(it)
    }
}

Nell'esempio, also viene utilizzato per il logging — mentre restituisce l'oggetto originale, esegue un effetto collaterale (output di un messaggio) senza modificare l'oggetto. Questa è una catena: validate(user) restituisce User, also registra il risultato e passa l'utente oltre. also è utile anche per aggiungere elementi a collezioni o per eseguire debug nel mezzo di una catena di trasformazioni.

let e run: trasformazioni e calcoli

let è una scope function che restituisce il risultato della lambda e passa il contesto tramite it. È la funzione principale per le trasformazioni null-safe: se l'oggetto è nullable, la combinazione ?.let { } esegue il blocco solo per valori non nulli. let viene anche utilizzato per limitare l'ambito di variabili temporanee e per trasformazioni simili a map da un tipo a un altro.

kotlin
fun findUser(id: Int): User?

val displayName = findUser(42)?.let { user ->
    "${user.name} (${user.email})"
} ?: "Unknown user"

// let con trasformazione di collezione
val numbers = listOf("1", "2", "3")
val parsed = numbers.firstOrNull()?.let {
    it.toIntOrNull()
} ?: 0

Nel primo esempio, let viene chiamato tramite ?. — il blocco viene eseguito solo se findUser ha restituito un valore non nullo. La lambda prende user tramite it (può essere rinominato per chiarezza) e restituisce una stringa formattata. Se l'utente non viene trovato, l'operatore Elvis ?: fornisce un valore predefinito. Nel secondo esempio, let converte una stringa in numero con un possibile null.

run è un analogo di let con accesso tramite this, che restituisce il risultato della lambda. run è comodo per calcoli che richiedono il contesto dell'oggetto ma restituiscono un nuovo valore. Chiamare run() senza oggetto (come run { ... }) crea un ambito temporaneo per le variabili. with è una non-estensione con accesso tramite this, adatta per raggruppare chiamate senza concatenamento.

kotlin
// run come calcolo basato sul contesto
val transformed = listOf(1, 2, 3).run {
    filter { it > 1 }.map { it * 10 }
}
println(transformed) // [20, 30]

// with per raggruppare chiamate
val info = with(StringBuilder()) {
    append("Name: ")
    append("Alice")
    toString()
}

Nell'esempio, run su una lista restituisce il risultato del filtraggio e del mapping — la trasformazione stessa, non la lista. with prende StringBuilder come argomento (non tramite punto) e internamente lavora con this: append, append, toString. Il risultato di with è la stringa info. La scelta tra run e with è spesso soggettiva, ma with è preferibile quando l'oggetto viene passato da un altro punto senza concatenamento.

with: contesto senza estensione

with si differenzia dalle altre scope functions in quanto non è una funzione di estensione. Prende l'oggetto come primo argomento e la lambda come secondo. All'interno della lambda, l'oggetto è accessibile tramite this. with è comodo per raggruppare chiamate di metodi dell'oggetto senza salvare il risultato o quando è necessario “entrare” temporaneamente in un contesto per leggere più proprietà.

kotlin
val user = User("Alice", "alice@example.com", "Admin")

val summary = with(user) {
    "User $name has role $role and email $email"
}
println(summary)

// with per inizializzazione componenti UI
with(TextView(context)) {
    text = "Hello"
    textSize = 18.0f
    setTextColor(Color.BLUE)
}

Nel primo esempio, with forma la stringa summary accedendo direttamente alle proprietà di user tramite this. Senza with, si dovrebbe scrivere "User ${user.name} has role ${user.role}" — ripetizione di user. Nel secondo esempio, with raggruppa la configurazione di TextView: tutti i metodi e le proprietà vengono chiamati su un singolo oggetto senza ripetizione. Svantaggio di with — impossibilità di usarlo in una catena, poiché non è un'estensione e non restituisce l'oggetto di contesto.

Domande frequenti

Quale scope function scegliere per inizializzare un oggetto?

Utilizzare apply — restituisce l'oggetto e fornisce l'accesso tramite this, consentendo di impostare le proprietà senza ripetere il nome dell'oggetto. also è adatto se è necessario un parametro it esplicito per distinguerlo dalle proprietà dell'oggetto.

In cosa differisce let da run?

let passa il contesto tramite it (parametro esplicito), run — tramite this (ricevitore implicito). let è comodo per controlli null e trasformazioni, run — per calcoli con contesto. Entrambi restituiscono il risultato della lambda.

Quando usare with invece di apply?

with non è un'estensione — prende l'oggetto come argomento. Utilizzare with per raggruppare chiamate quando non è necessario il concatenamento. apply è un'estensione che restituisce l'oggetto, comodo in inizializzatori e pattern builder.

Si può annidare una scope function dentro un'altra?

Sì, ma fare attenzione a this — this annidato crea confusione. Utilizzare let o also con nomi espliciti per contesti annidati per evitare ambiguità quando si fa riferimento all'oggetto esterno.

Quale scope function è la più performante?

Tutte le scope functions sono inline, quindi non creano overhead da classi anonime. La differenza di prestazioni tra di loro è trascurabile. La scelta dovrebbe basarsi sulla leggibilità e le convenzioni, non sulla velocità.

Riepilogo

  • Scope functions — cinque funzioni inline di Kotlin (let, run, with, apply, also) per eseguire codice nel contesto di un oggetto
  • apply restituisce l'oggetto di contesto con accesso tramite this — ideale per l'inizializzazione di proprietà
  • also restituisce l'oggetto di contesto con accesso tramite it — adatto per logging ed effetti collaterali
  • let restituisce il risultato della lambda con accesso tramite it — base per trasformazioni null-safe
  • run restituisce il risultato della lambda con accesso tramite this — comodo per calcoli con contesto
  • with non è un'estensione e restituisce il risultato della lambda — adatto per raggruppare chiamate senza concatenamento
  • Combinazione di caratteristiche (this/it e restituzione oggetto/risultato) determina la scelta della scope function per ogni attività

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