Al di là della modalità generica di definizione di una classe, Kotlin prospetta
diversi tipi specifici di classi che il programmatore può impiegare a seconda degli
utilizzi che deve farne.
Per ognuna di esse, i progettisti del linguaggio hanno attuato accorgimenti atti a renderle utilizzabili al
massimo, sebbene in molti casi abbiano applicato loro delle limitazioni.
In questa lezione, presentiamo una carrellata di tali casistiche.
Il primo tipo di cui parliamo sono le data class, classi
progettate al solo scopo di contenere un insieme di dati da aggregare. Tale
caso, particolarmente diffuso nella Programmazione Orientata agli Oggetti, vede
l'impiego della parola chiave data
prima di class
al momento
della definizione. La seguente riga è sufficiente a creare una data class
per
contenere i dati relativi ad una persona:
data class Persona(val nome:String, val cognome:String, val eta:Int)
Potrebbe già così essere impiegata nel seguente modo per dichiarare un oggetto:
val unTizio=Persona("Giulio", "Rossi", 25)
Tutti i membri definiti tra le parentesi tonde diventano le proprietà della
classe e costituiscono gli argomenti del costruttore primario. Abbiamo utilizzato
per tutti la parola chiave val
, che li rende valori immutabili: un'assegnazione come
unTizio.nome="Paola"
, infatti, restituirebbe errore. Tuttavia, non vi è alcun divieto
di utilizzare var
per uno o più argomenti. Il seguente codice, ad esempio,
potrebbe essere eseguito correttamente producendo in output il messaggio "Marcello Rossi ha 25 anni":
data class Persona(var nome:String, val cognome:String, val eta:Int)
fun main(args:Array<String>) {
val unTizio=Persona("Giulio", "Rossi", 25)
unTizio.nome="Marcello"
print("${unTizio.nome} ${unTizio.cognome} ha ${unTizio.eta} anni")
}
Le enum class prendono il classico ruolo delle enum
in molti altri
linguaggi: permettono di definire una classe i cui valori possono appartenere solo ad un
set predefinito. Ad esempio, una classe che rappresenta lo stato di un semaforo può solo
assumere i valori "rosso", "verde" e "giallo", pertanto la sua definizione mediante
enum class
risulterebbe sicuramente appropriata:
enum class Semaforo{
ROSSO,
VERDE,
GIALLO
}
Un esempio di utilizzo potrebbe essere questo:
val semaforoAttuale=Semaforo.GIALLO
when(semaforoAttuale){
Semaforo.ROSSO->println("Fermati!")
Semaforo.GIALLO->println("Rallenta, diventerà ROSSO presto")
Semaforo.VERDE->println("Passa pure")
}
Le sealed class servono a definire una classe base per una ristretta
gerarchia di oggetti. Ad esempio, potremmo pensare ad una generica struttura di classi per connotare
le tipologie di beni personali attribuibili ad un individuo:
sealed class Proprieta()
data class Casa(val indirizzo:String, val categoriaCatastale:String, val valore:Float) : Proprieta()
data class Veicolo(val tipologiaVeicolo: String, val marca: String,val modello: String, val valore:Float) : Proprieta()
In questo modo potremmo creare strutture dati di oggetti di tali classi e gestirle tutte con la medesima
politica:
val beniPersonali= listOf<Proprieta>(
Casa("via dei Noci, 54, Roma", "A3", 220000F),
Casa("via Torino, 25, Roma", "A3", 140000F),
Veicolo("autovettura", "Volkswagen","T-Roc", 25000F)
)
beniPersonali.forEach {
/*
* operazione da svolgere su ogni oggetto di classe derivata
* da Proprieta
*/
}
Le classi sealed
sono sottoposte ad alcuni vincoli. Innanzitutto sono astratte, e pertanto non è possibile
istanziare oggetti a partire da esse. In secondo luogo, le classi da esse derivate devono essere definite nello stesso file.
Tale vincolo tuttavia vale solo per le classi derivate di "prima generazione", quelle derivate da queste ultime e tutte le loro
discendenti possono essere collocate ovunque nel progetto.
In Kotlin è sempre possibile dichiarare una classe all'interno di un'altra, creando così delle classi annidate:
class Esterna{
val proprieta="valore della proprietà"
/*
* membri della classe esterna
*/
class Annidata{
/*
* membri della classe esterna
*/
}
}
Al momento del suo utilizzo, si dovrà però specificare che si tratti di una classe annidata:
val oggettoClasseAnnidata=Esterna.Annidata()
La classe interna può accedere a elementi di classe esterna solo se viene marcata come
inner
:
class Esterna{
val proprieta="valore della proprietà"
inner class Annidata{
val proprietaClasseInner=proprieta
}
}