Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Tipi di dato e variabili su Kotlin

Conoscere i principali tipi di dato disponibili su Kotlin, il meccanismo della type inference e la gestione dei valori null.
Conoscere i principali tipi di dato disponibili su Kotlin, il meccanismo della type inference e la gestione dei valori null.
Link copiato negli appunti

In Kotlin, ogni informazione viene trattata mediante oggetti. Anche quando useremo
numeri, valori booleani o semplici caratteri, potremo sempre invocare i metodi che essi mettono a disposizione.
In realtà, la distinzione tra valori di tipo primitivo e oggetti - basilare in linguaggi come Java o C++ - esiste
anche in Kotlin ma è meno apparente: risiede infatti nella rappresentazione in memoria (trattata nel seguito della
lezione) e non nella definizione del tipo di dato, aspetto che ne esce uniformato.

Tipi di dato fondamentali

Illustriamo subito le classi utilizzate per la definizione delle più importanti tipologie di
informazione, distinguendole per categorie:

  • numeri interi: abbiamo le classi fondamentali Byte, Short, Int e Long
    che tratteranno, rispettivamente, numeri definiti su 1, 2, 4 e 8 byte. Quando specifichiamo un numero long,
    dobbiamo etichettarlo con il suffisso L (esempio: con 100L esprimiamo un valore 100 come long). Possono essere espressi nella notazione
    decimale, esadecimale e binaria, mentre quella ottale non è contemplata
  • numeri decimali (con la virgola): per questa categoria abbiamo le classi Float (4 byte) e
    Double (8 byte). I numeri con la virgola, di default, sono sempre interpretati come double, pertanto saranno intesi come float
    solo se etichettati con il suffisso f (esempio: 1.5f)
  • booleani: con la classe Boolean possiamo rappresentare i valori logici
    true e false
  • caratteri: la classe Char definisce un carattere singolo, che non può essere
    trattati direttamente come numero (a differenza di quanto avviene, ad esempio, in C) senza un'opportuna conversione
  • array: si basano su una classe parametrizzata, la classe Array. Si può usare la sintassi
    delle parentesi quadre (utilizzata in diversi linguaggi), sebbene in realtà si tratta di uno shortcut verso metodi
    setter e getter. Con la proprietà size è inoltre possibile ottenere il numero di elementi contenuti nell'array
  • stringhe: sono trattate con la classe String e definite con i doppi apici.
    Accettano caratteri di escape introdotti dal simbolo backslash (\) per introdurre comportamenti
    speciali: ad esempio, \n indica il ritorno a capo. Esiste una tipologia particolare di stringa che viene introdotta
    con tre doppi apici e permette di essere scritta su più righe, ma non accetta i caratteri di escape. Ecco un esempio:
    print("""
        Questa è una stringa
        su più righe
        e gli escape tipo \n non funzionano
        """)

Dichiarazione di variabili

Una dichiarazione di variabili in Kotlin viene introdotta dalla parola chiave val se il suo valore deve essere immutabile (non può cambiare dopo l'inizializzazione)
o da var se può mutare:

// questa non si potrà modificare
val numeroImmutabile=100
// possiamo cambiarne il valore
var numeroMutabile=100
// assegnazione valida
numeroMutabile=150
// ERRORE ... non possiamo modificare numeroImmutabile
numeroImmutabile=200

Si noti che in nessuno dei due casi abbiamo indicato il tipo di dato. Kotlin, in questi casi, applica il meccanismo
della type inference, per cui riesce a intuire il tipo di dato più adatto per il valore passato in inizializzazione.
Nei casi in cui volessimo specificare un tipo di dato, possiamo utilizzare la seguente notazione:

val numeroImmutabile:Int=100
var numeroMutabile:Float=1.5f

L'inizializzazione può essere anche rimandata ad un momento successivo alla dichiarazione della variabile, ma
in tal caso è necessario applicare sin da subito un tipo di dato:

var valore:Int
valore=5

Valori nulli e null-safety

In questo discorso, subentra un argomento molto importante: la potenziale nullità di un riferimento.
In Kotlin, si cerca di evitare le NullPointerException. A tale fine, il linguaggio implementa una serie di politiche che evitano il verificarsi di questo tipo di eccezioni.

Qualsiasi riferimento in un programma Kotlin può ricadere in una delle due seguenti tipologie:

  • non-nullable: vengono inizializzati al momento della dichiarazione della variabile
    e non possono più avere valore null. Ad esempio, la seguente variabile nasce con un valore acquisito, pertanto
    il tentativo successivo di annullarla verrà segnalato a livello di compilazione con il messaggio di errore
    "Null can not be a value of a non-null type String" ("Null non può essere un valore di un tipo String non-nullo"):
    var frase="Ciao a tutti"
    // ERRORE ... assegnazione non valida
    frase=null
  • nullable: può essere anche inizializzato in fase di dichiarazione, ma specificheremo il tipo di dato
    seguito da un punto interrogativo, ad indicare l'annullabilità del valore. Nel seguente esempio, l'assegnazione di null alla
    variabile sarà consentita:
    var frase:String?="Buongiorno a tutti.."
    // assegnazione valida
    frase=null

Nella manipolazione dei riferimenti non-nullable non dovremo temere NullPointerException, mentre
attueremo prevenzione su quelli nullable sfruttando una delle varie sintassi che Kotlin offre in merito:

  • si può utilizzare l'if come una sorta di operatore ternario, sottoponendo a verifica
    la nullità del valore e ricevendo in output uno tra più risultati alternativi. Il seguente codice verifica se la stringa
    è nulla, e se non lo è ne stampa una sottosequenza, altrimenti stampa "*** NULLO ***":
    var frase:String?="Buongiorno a tutti.."
    frase=null
    // stampa *** NULLO *** perchè frase=null
    val sottostringa=if (frase==null) "*** NULLO ***" else frase.decapitalize()
    println(sottostringa)
  • possiamo utilizzare l'operatore ?. che, in caso di riferimento
    nullo, restituirà null senza lanciare eccezioni:
    // se frase=null restituisce null altrimenti la lunghezza della stringa
    val lunghezza=frase?.length
  • con la funzione let indichiamo di eseguire un blocco di codice solo se un oggetto non è nullo.
    Nel seguente frammento, solo la non nullità del riferimento frase permette di eseguire il
    blocco di codice introdotto da let:
    // frase è nullable
    	var frase:String?="Domani è un altro giorno"
    	frase?.let{
    		/*
    		 *  Codice che sarà eseguito solo se
    		 *  la variabile frase non ha valore nullo
    		 */
    	}
  • per i casi in cui volessimo deliberatamente una NullPointerException in caso di riferimento nullo,
    esiste l'operatore !!:
    /* restituisce la lunghezza se frase non è null
    	 altrimenti lancia una NullPointerException*/
    	 val lunghezza=frase!!.length
  • il cosiddetto Elvis operator evita la NullPointerException richiedendo solo
    un risultato di default da restituire. Nel caso seguente, se frase è impostato a null, viene restituito
    "NULLO" senza alcuna eccezione; altrimenti, verrà restituito il risultato del metodo:
    val res= frase?.decapitalize() ?: "NULLO"

Rappresentazione dei dati

Il modo in cui una variabile Kotlin viene dichiarata determina, in molti casi, come questa verrà
rappresentata in memoria. Ciò si verifica per tutte le classi che in Java potrebbero essere
rappresentate con tipi primitivi (numeri interi, in virgola, caratteri e booleani). Infatti, se
l'oggetto viene inizializzato immediatamente, esso sarà rappresentato come un tipo di dato primitivo,
altrimenti sarà "inscatolato" in un oggetto. Si osservi il seguente codice:

val numero:Int=34
    var altroNumero: Int? =null
    altroNumero=44

Nell'immagine seguente vediamo la rappresentazione risultante nel debugger di IntelliJ:

Figura 1. Valori e oggetti visti dal debugger (click per ingrandire)

Valori e oggetti visti dal debugger

Il primo intero, subito inizializzato, è stato reso int (tipo primitivo in Java), il secondo, nullable, dichiarato ma non
inizializzato subito, è stato inscatolato in un Integer.

Ti consigliamo anche