[Kotlin Tip] Utilizzare le extension
Vi è mai capitato di lavorare con una libreria di terze parti ed avere la necessità di aggiungere funzionalità o implementare “comportamenti” custom utili alle vostre esigenze?
O magari di ritrovarvi a lavorare ad un progetto pieno zeppo di classi di utilità che sopperiscono alle mancanze della Java API o delle succitate librerie di terze parti…
Kotlin permettere di risolvere queste due problematiche in maniera estremamente elegante, senza dover ricorre ad ereditarietà o classi statiche.
La soluzione si chiama extension.
Extension function
Le extension function permettono di aggiungere funzionalità a classi “di altri” senza scomodare l’ereditarietà. Tutto questo in modo estremamente espressivo e leggibile.
Partiamo con un esempio molto concreto: di recente, lavorando con i repository di Spring Data, mi sono reso conto che alcuni metodi restituiscono oggetti Optional
(ad es. findById
), utilissimi in Java, ma poco pratici se stiamo lavorando con Kotlin.
Ho quindi implementato una funzione d’estensione per convertire un Optional
in un tipo nullable:
Osserviamo la dichiarazione appena riportata:
-
Il tipo
Optional
in questo caso si definisce receiver type — o semplicemente receiver —, ovvero, il tipo che viene esteso — che riceve l’estensione. -
All’interno del corpo della funzione ci riferiamo all’istanza del receiver — receiver object — con la parola chiave
this
, proprio come se stessimo definendo la nuova funzione all’interno della classeOptional
— o di una classe da essa ereditata.
Ma la cosa veramente ganza è il fatto di poter invocare questa funzione esattamente come se fosse parte della classe originaria:
NOTA In realtà, ho poi scoperto che Spring Data, dalla versione 2.1.4, include una fantastica extension function per i suoi CrudRepository
che permette di lavorare con i tipi nullable: la funzione è findByIdOrNull
.
Extension property
Esattamente come per le funzioni è possibile estendere una qualunque classe con delle property custom:
Il campo isEven
così definito sarà ora disponibile per tutti gli oggetti di tipo Int
:
Il fatto che nella property abbia definito solo il get
non è un caso, infatti le extension property non supportano l’assegnazione. Il perché di questo appare chiaro una volta capito che…
… le extension sono metodi statici
Proprio così: definendo un’estensione non andiamo ad aggiungere effettivamente una nuova funzione membro in una classe. Piuttosto — dietro le quinte — andiamo a definire un metodo statico che accetta il receiver object come suo primo argomento.
Vediamo un altro esempio per capire meglio. Supponiamo di definire la seguente extension function nel file Utilities.kt
:
che ovviamente invocheremo così:
Se però volessimo usare questa funzione in un metodo Java ci accorgeremmo subito della sua natura “statica”, infatti dovremmo invocarla in questo modo:
Allo stesso modo, per invocare una extension property in Java avremo qualcosa di simile:
In questo esempio ho supposto di voler utilizzare la property isEven
definita qualche paragrafo fa. L’ipotetico file Kotlin IntUtils
in cui abbiamo dichiarato la property, viene trasformato in Java nella classe IntUtilsKt
.
Come vedete, in Java, anche l’accesso ad una extension property si traduce nell’invocazione di un metodo statico.
Nel nostro esempio, di fatto, stiamo invocando un getter dell’oggetto 10
, anche se in modo un po’… strano.
In conclusione
Le extension rappresentano sicuramente un’interessante peculiarità di Kotlin, ma come molti altri aspetti del linguaggio, che a prima vista sembrano una panacea — soprattutto per chi è abituato a lavorare con Java —, vanno usate con parsimonia e saggezza.
Un abuso indiscriminato di funzioni e property d’estensione può portare a codice frammentato e poco coerente; in alcuni casi può aver senso ricorrere alla vecchia, cara — e forse fin troppo bistrattata — ereditarietà.
Alla prossima,
David