ProgrammazioneSviluppatore Kotlin di livello intermedio

Che cos'è una funzione di scope in Kotlin (let, also, run, apply, with)? Qual è la differenza tra queste funzioni, come sceglierle per compiti diversi e quali aspetti possono sorgere durante il loro utilizzo? Fai degli esempi.

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Le funzioni di scope in Kotlin sono funzioni standard (let, also, run, apply, with) che consentono di gestire il contesto di esecuzione di un blocco di codice per un oggetto. Esse differiscono per:

  • tipo di valore restituito,
  • modo in cui l'oggetto è accessibile all'interno del blocco: tramite it o this.

Riepilogo comparativo:

Funzionethis/itRestituiscePer cosa?
letitrisultatocatena di operazioni, lavoro con nullable, mappa
alsoitoggettoeffetti collaterali, logging, debug
runthisrisultatocalcoli, inizializzazione con restituzione
applythisoggettoconfigurazione dell'oggetto, builders
withthisrisultatolavoro con API esterne, oggetto "esterna"

Esempi:

  • let: comodo se l'oggetto è nullable:
val str: String? = "Text" str?.let { println(it.length) }
  • apply: configurazione dell'oggetto:
val paint = Paint().apply { color = Color.RED strokeWidth = 2f }
  • run: esecuzione su un oggetto, restituisce il risultato:
val length = "abcde".run { length }
  • with: per lavorare con un oggetto esterno:
val sb = StringBuilder() with(sb) { append("Hello, ") append("world!") toString() }
  • also: per effetti collaterali (ad esempio, log):
val list = mutableListOf(1, 2, 3) list.also { println("Prima: $it") }.add(4)

A cosa prestare attenzione:

  • let crea una copia dell'oggetto in it, non è molto comodo cambiare le proprietà dell'oggetto.
  • apply e also restituiscono sempre l'oggetto stesso (this / it), utile per i builders.
  • run/with si confondono spesso: with è una funzione normale, non un'extension.

Domanda trabocchetto.

Qual è la differenza tra let e also?

Risposta:

  • Entrambi usano it all'interno del blocco,
  • let restituisce il risultato della lambda, spesso utilizzato per catene di trasformazione,
  • also restituisce l'oggetto originale, utilizzato per effetti collaterali (log, debug), così da non intervenire nella catena di trasformazione.

Esempio:

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

Esempi di errori reali dovuti alla mancanza di conoscenza delle sottigliezze dell'argomento:


Storia

Un principiante ha usato let per configurare un oggetto, pensando che così potesse cambiare il suo stato "in catena". Di conseguenza, al termine del blocco di configurazione otteneva non l'oggetto, ma il risultato della lambda (ad esempio, nulla), rompendo così la catena di costruzione del DSL.


Storia

Durante la scrittura del codice per lavorare con oggetti nullable, hanno usato run invece di let, non notando la differenza nel valore restituito. Di conseguenza, il risultato dell'espressione differiva da quello atteso, apparivano null dove non avrebbero dovuto esserci — la logica dell'applicazione si rompeva.


Storia

In un grande builder, hanno accidentalmente usato with per oggetti interni, sperando nel pattern extension. Poiché with non è una funzione di estensione, la catena di più blocchi with non funzionava correttamente, le chiamate interne si confondevano e uscivano dai confini dell'oggetto attuale. È stato necessario riscrivere completamente la gerarchia di creazione degli oggetti.