Kotlin, linguaggio estremamente moderno, si è adoperato al massimo per accogliere aspetti innovativi tipici dei linguaggi attuali
anche in fatto di funzioni. Su questo tema vi sono un paio di caratteristiche che permettono di imprimere
al nostro codice una sintassi più sintetica senza perdere di significatività:
- extension function, che permettono di aggiungere "al volo" un nuovo metodo agli oggetti di una classe
esistente; - espressioni lambda, con cui si potrà innestare un blocco di codice da eseguire in un determinato meccanismo.
Affrontiamo nel dettaglio entrambe queste caratteristiche.
Extension function
Come si è già visto in più punti della guida, una funzione si dichiara in Kotlin con la parola chiave fun
e può essere impiegata sia internamente ad una classe - in qualità di membro - sia esternamente come funzione in
senso più "classico". Parallelamente a questo, abbiamo visto che per integrare nuove funzionalità in una
classe si può fare ricorso all'ereditarietà, sebbene questo - a livello concettuale - non significhi solo creare "oggetti con più
metodi" ma definire un nuovo tipo di informazione più specifica rispetto a quanto espresso dalla classe base.
Le extension function si inseriscono in questo contesto proponendosi come un modo per arricchire un oggetto di
nuovi metodi senza necessità di estendere la classe. Tecnicamente, possono essere visti come metodi definiti all'esterno di
una classe ma associati ad essa.
Ad esempio, immaginiamo di voler aggiungere alla classe String
un ulteriore metodo di nome toPartialUpperCase
che
si affianchi al già esistente toUpperCase
: mentre quest'ultimo restituisce una versione interamente maiuscola di una stringa,
il nostro metodo ne restituirà una parzialmente maiuscola, solo a partire da una determinata posizione. Questo il codice:
fun String.toPartialUpperCase(inizio:Int): String
{
if (inizio<0 || inizio>=this.length)
return this
return this.substring(0,inizio)+this.substring(inizio).toUpperCase()
}
La nuova funzione riceve un argomento intero che indica da quale posizione la stringa prodotta
dovrà essere maiuscola. Nel caso in cui tale parametro fosse minore di 0 o maggiore o uguale della
lunghezza della stringa verrebbe restituita una stringa uguale a quella di partenza. Quali sono gli aspetti particolari di
questa funzione?
- Il nome della funzione è anticipato da
String.
: ciò significa che, nonostante essa non
sia inclusa nella definizione diString
, nel nostro programma potrà essere invocata su oggetti di questo tipo - All'interno del metodo si nomina
this
, e ciò si riferisce alla stringa su cui la funzione viene chiamata
Per provare la nostra nuova funzione basterà invocarla su un oggetto di classe String
:
fun main(args:Array<String>) {
val frase="Domani è un altro giorno"
val parzialmenteMaiuscola=frase.toPartialUpperCase(12)
println(parzialmenteMaiuscola)
}
L'output sarà il seguente:
Domani è un ALTRO GIORNO
Le extension function sono una caratteristica molto potente di Kotlin, della quale però non è consigliabile
abusare. Si consideri che si tratta di una "deroga" ai normali flussi della progettazione orientata agli
oggetti, che risulta però molto utile per aggiungere funzionalità non eccessivamente invasive in una classe
di cui non siamo autori, evitando così di disperdere codice in funzioni collocate in vari punti del progetto.
Espressioni lambda
Le espressioni lambda sono state
una delle novità salutate con maggior entusiasmo nella versione 8 di Java se non fosse altro perché si tratta di un elemento già importante
di molti altri linguaggi di programmazione, alla base della programmazione funzionale. Si tratta di espressioni estremamente sintetiche
che permettono di specificare il codice che in un dato momento dovrà essere eseguito. La loro compattezza è dovuta al fatto
che il programmatore non dovrà indicare altro che la porzione da personalizzare di uno specifico meccanismo. Ad esempio,
gli array sono dotati del metodo filter
, che permette di filtrare gli oggetti che essi contengono in base ad un particolare criterio.
Quest'ultimo consiste in una valutazione che restituisce un valore booleano: ogni oggetto dell'array viene passato a questo codice
e solo se il confronto risulta vero il valore viene accettato nel risultato. Ma come passare il codice della valutazione? Si può fare mediante, un'espressione lambda, appunto.
Vediamo un esempio:
val numeri= intArrayOf(12, -34, -9, 85, 15, 20, -7, 38)
val maggioriDiZero=numeri.filter { x:Int -> x>0 }
println(maggioriDiZero)
Il codice appena visto dichiara un array di interi contenente alcuni valori negativi. All'interno di
filter
viene passato il criterio di selezione degli elementi maggiori di zero. Le caratteristiche
che distinguono questa espressione lambda sono:
x:Int
dice che gli elementi dell'array, uno alla volta, saranno valutati come
una variabile di tipoInt
. Nel codice che svolge la valutazione verrà chiamato x il generico elemento dell'array;- l'operatore
->
è ciò che contraddistingue le espressioni lambda. Il suo compito è distinguere
i parametri, usati come argomenti, ed il blocco di codice; x>0
è il vero e proprio confronto da operare. Restituisce il risultato della comparazione tra x (il generico elemento
dell'array) e zero. Tutti i numeri per cui tale valutazione rispondetrue
vengono inseriti nel risultato finale.
Ecco il risultato che si ottiene:
[12, 85, 15, 20, 38]
Degli otto valori iniziali, tre erano negativi pertanto sono stati scartati. Il funzionamento quindi
è andato a buon fine. L'array restituito contiene solo i cinque valori maggiori di zero.
Il codice avrebbe funzionato perfettamente anche se l'avessimo scritto in un altro modo:
val maggioriDiZero=numeri.filter { it>0 }
Siamo passati da una espressione lambda di questo tipo x:Int -> x>0
, a it>0
. Ciò si è potuto fare in
quanto l'espressione utilizza un singolo parametro pertanto non è necessario specificare nel codice a "quale" argomento ci stiamo riferendo.
La parola chiave it si riferisce univocamente all'unico argomento dell'espressione indipendentemente dal tipo di codice che svolgerà.
Ciò sintetizza ulteriormente il codice evitando persino l'impiego dell'operatore freccia.