Tutorial per Kotlin: i primi passi con il nuovo linguaggio di programmazione
La versione 1.0 di Kotlin è disponibile solo dal 2016, ma è già molto apprezzata perché rappresenta una valida alternativa a Java. Questo linguaggio di programmazione basato su oggetti è stato sviluppato da JetBrains, una società ceca di sviluppo software. Convince molte persone per il suo carattere snello e per il numero ridotto di errori di runtime che produce, in particolare le temute NullPointerExceptions. Kotlin è particolarmente apprezzato nello sviluppo di applicazioni Android, ma anche come punto di partenza per le applicazioni JavaScript, dove il giovane linguaggio di programmazione gode di enorme popolarità.
Kotlin è fondamentalmente basato su Java: sebbene i due linguaggi di programmazione non siano compatibili, Kotlin viene convertito in bytecode, che può essere letto da una Java Virtual Machine (JVM).
Kotlin è fondamentalmente basato su Java: sebbene i due linguaggi di programmazione non siano compatibili, Kotlin viene convertito in bytecode, che può essere letto da una Java Virtual Machine (JVM).
Istruzioni per usare Kotlin con esempi
Per iniziare con Kotlin potete scaricare il compilatore dal sito ufficiale. La cosa migliore è utilizzare un ambiente di sviluppo (IDE), come ad esempio IntelliJ IDEA (sempre di JetBrains), Eclipse (con relativo plug-in), NetBeans e Android Studio, che supportano Kotlin.
I produttori di Kotlin mettono a disposizione una sandbox online dove potete provare tutti gli esempi.
Pacchetti (packages)
All'inizio di un progetto dovete importare i pacchetti necessari per la sua realizzazione e definire il pacchetto su cui state lavorando. I pacchetti contengono classi e funzioni.
package test.bar
import foo.bar
import footoo.bar as footoo
Per evitare problemi con nomi identici è possibile rinominare i pacchetti in Kotlin con as. L'assegnazione dei nomi ai pacchetti non deve seguire la struttura di directory in cui si trovano, ma si raccomanda comunque questa prassi per motivi di chiarezza.
Kotlin carica automaticamente i pacchetti più importanti in ogni progetto.
A differenza di Java, Kotlin permette anche di importare singole funzioni da altri pacchetti. Per farlo dovete specificare il percorso corretto:
import foo.bar.myFunction
La funzione può quindi essere utilizzata normalmente.
Le righe di codice in Kotlin non richiedono più l'uso di terminatori, come un punto e virgola.
Variabili
Kotlin prevede due diversi tipi di variabili: quelle che indicano un riferimento immutabile e non sono modificabili, introdotte con val, e quelle il cui valore può essere modificato nel corso del programma, che vengono introdotte con var.
val name = "Clara Oswald"
var age = 22
A differenza del nome, che è costante, l'età può essere modificata, ad esempio in una funzione.
In questo esempio, Kotlin ha impostato autonomamente il valore della variabile. È anche possibile etichettare specificamente questi tipi di base.
Tipi di base (basic types)
Kotlin lavora con specifici tipi di variabili e classi. In Kotlin ogni tipo consiste in un oggetto, il che lo rende leggermente diverso da Java. Mentre il linguaggio di programmazione più vecchio deve prima impacchettare i tipi primitivi in una classe wrapper per renderli oggetti, in Kotlin non è necessario perché tutti i tipi sono già oggetti.
Numeri (numbers)
In Kotlin è possibile inserire numeri senza un marcatore specifico: il compilatore capisce che si tratta di valori numerici. Le virgole sono indicate da punti. È possibile utilizzare anche numeri esadecimali. Per una migliore leggibilità è possibile visualizzare i separatori decimali mediante trattini bassi. Kotlin conosce diversi tipi di numeri, ognuno dei quali può avere una dimensione massima diversa:
- Long: 64 bit
- Int: 32 bit
- Short: 16 bit
- Byte: 8 bit
- Double: 64 bit
- Float: 32 bit
val myNumber: Long = 40
È possibile convertire un numero di un tipo in un altro.
val myInt = 600
val myLong= myInt.toLong()
Il comando toLong converte il valore Int in un valore Long. Il comando funziona allo stesso modo per gli altri tipi di numeri.
String
Quando parliamo di string intendiamo una parola o una frase intera, ovvero una stringa di caratteri. Per utilizzarli in Kotlin inserite il testo scritto tra virgolette doppie. Se volete inserire più righe di testo, sono necessarie tre virgolette doppie sia all'inizio che alla fine (Raw String).
val myString = "Questo è uno string singolo."
val myLongString = """Questo è uno string
su più righe."""
Come in molti altri linguaggi di programmazione, anche Kotlin supporta le sequenze di escape: anteponendo un backslash davanti a un carattere indicate che questo non appartiene alla stringa vera e propria e che deve essere trattato come un carattere di controllo. Con l'aiuto del backslash potete inserire nella stringa anche caratteri che hanno un significato diverso in Kotlin. Queste sono le sequenze di escape possibili:
- \t: tab
- \b: backspace
- \n: nuova riga
- \r: carriage return (ritorno a capo)
- \': virgoletta singola
- \": virgoletta doppia
- \\: backslash
- \$: simbolo del dollaro
val author = "Sandra"
val myString = "Questo testo è di $autore"
Caratteri (characters)
In aggiunta agli string per i singoli caratteri Kotlin fornisce anche il tipo di dati speciale character. Questi vanno tuttavia inseriti tra virgolette singole piuttosto che tra quelle doppie.
var model = 'A'
Booleano
Il basic type boolean può assumere soltanto due valori, vero (true) o falso (false).
Array
In Kotlin un array è una raccolta di dati. Un array è formato da arrayOf() o Array(). La prima funzione è semplice:
val myArray1 = arrayOf(0, 1, 2, 3, 4, 5)
In questo modo viene creato un array con le cifre da 1 a 5. In queste raccolte possono essere incluse anche altri tipi di string e booleani, anche misti. Se si vuole limitare l'array a un solo tipo, lo si specifica nella funzione.
val myArray2 = arrayOf<int>(10, 20, 30)</int>
val myArray3 = booleanArrayOf(true, true, false)
Il costruttore Kotlin Array() è più complesso: qui è necessario specificare anche la lunghezza e una funzione lambda.
val myArray4 = Array(6, { i -> i })
Il costruttore crea un array a sei cifre partendo da zero: 0, 1, 2, 3, 4, 5.
L'argomento relativo a costruttori e lambda sarà approfondito più avanti.
Ogni inserimento in un array è indicizzato e può essere chiamato tramite questo indice. A tale scopo vengono utilizzate le parentesi quadre nelle quali viene specificata la posizione dell'inserimento nell'elenco.
fun main() {
val myArray5 = arrayOf("Jan", "Maria", "Samuel")
println(myArray5[2])
}
Il risultato della funzione in questo caso sarà "Samuel", dato che il conteggio inizia da 0.
Operatori
Come molti altri linguaggi di programmazione, anche Kotlin lavora con diversi operatori che potete integrare nel vostro codice sorgente. Questi includono operatori matematici (+, -, *, /, %), operatori di confronto (<, >, <=, >=, ==, !=) e operatori logici (&&, ||, !). Strettamente legati agli operatori sono le keyword, ovvero termini che hanno un significato fisso in Kotlin che non può essere cambiato.
Nella documentazione ufficiale di Kotlin potete trovare una lista completa di tutte le keyword e degli operatori.
Range
In Kotlin un range descrive un tipo che va da un determinato punto a un altro. Per creare un range si utilizza l'operatore .. o le funzioni rangeTo() o downTo(). La variante con i due punti è incrementale, mentre con le due funzioni si definisce una direzione.
val range1 = 1..5
val range2 = 1.rangeTo(5)
val range3 = 5.downTo(1)
In queste versioni semplici sono sufficienti step con valore 1 per creare un range. Se invece volete modificare il valore degli step dovete utilizzare la relativa funzione step.
val range4 = 0..10 step(2)
Per indirizzare dati singoli nel range, utilizzate l'operatore in. In questo modo potete per esempio creare espressioni o cicli. Per verificare se un valore non appartiene al range viene utilizzato l'operatore !in.
val range5 = 0..10
fun main() {
for (n in range5) {
println(n)
}
if (7 in range5) {
println("yes")
}
if (12 !in range5) {
println("no")
}
}
L'argomento relativo a funzioni, cicli ed espressioni sarà approfondito più avanti.
Funzioni (functions)
Le funzioni in Kotlin vengono sempre create mediante il comando fun. Dopodiché vengono definiti il nome della funzione, gli argomenti contenuti e il modo in cui agisce.
fun div(a: Int, b: Int): Int {
return a/b
}
fun main() {
println(div(100, 2))
}
Innanzitutto definiamo la funzione div (che sta per divisione) mediante due parametri Int, a e b. Compito della funzione è quello di fornirci il risultato della divisione tra a e b, sempre sotto forma di variabile Int. In seguito chiamiamo la funzione precedentemente definita nella funzione main, le assegniamo valori concreti e visualizziamo il risultato nella console mediante println (print line). Kotlin esegue il contenuto della funzione main() automaticamente. Questa funzione rappresenta il punto di partenza in un programma Kotlin.
Kotlin non accetta comandi oltre alle funzioni. Sono ammesse solo dichiarazioni.
In Kotlin è possibile rappresentare le funzioni composte da una sola riga di codice in modo semplificato. Invece di aprire una parentesi graffa, scrivere una nuova riga e chiudere la parentesi, si usa un segno uguale e si rinuncia al comando return.
fun div(a: Int, b: Int): Int = a/b
fun main() = println(div(100, 2))
Quando definite una funzione potete indicare valori di default per evitare che parametri mancanti causino errori. In questo modo se lasciate vuoti i rispettivi parametri, verranno utilizzati i valori di default.
fun div(a: Int = 10, b: Int = 5): Int = a/b
fun main() = println(div())
Lambda
Una funzione lambda (o funzione anonima) è una funzione che non appartiene ad alcuna classe o oggetto. Le funzioni lambda vengono inserite direttamente in altre funzioni o variabili e chiamate senza l'uso della keyword fun. Le funzioni lambda vengono sostanzialmente utilizzate e create come le variabili del tipo val.
fun main() {
val myMessage = { println("Hello world!") }
myMessage()
}
In Kotlin le espressioni lambda devono essere inserite sempre tra parentesi graffe e possono elaborare anche argomenti di funzioni. Queste sono contrassegnate da una freccia che separa i parametri dal nucleo dell'espressione.
fun main() {
val div = {a: Int, b: Int -> a/b}
println(div(6,2))
}
Classi (classes)
Proprio come in Java, anche in Kotlin le classi sono raccolte di dati e funzioni. Per definire una classe è sufficiente inserire la keyword class. In seguito è possibile aggiungere informazioni alla classe.
class Tardis {
var year: Int
var place: String
constructor(year: Int, place: String) {
this.year = year
this.place = place
}
}
In Kotlin il costruttore rappresenta una funzione necessaria per la creazione di oggetti. A tale scopo il linguaggio di programmazione distingue tra costruttori primari e secondari. I costruttori primari sono una pratica scrittura abbreviata, mentre quelli secondari ricordano molto la scrittura di altri linguaggi a oggetti, come Java. L'esempio summenzionato mostra la seconda variante.
È possibile comunque omettere il costruttore secondario e inserire al suo posto un costruttore primario, che viene specificato direttamente nell'intestazione della classe e ne indica, allo stesso tempo, i parametri, riducendo notevolmente il numero di righe di codice.
È possibile comunque omettere il costruttore secondario e inserire al suo posto un costruttore primario, che viene specificato direttamente nell'intestazione della classe e ne indica, allo stesso tempo, i parametri, riducendo notevolmente il numero di righe di codice.
class Tardis constructor(var year: Int, var place: String)
Se non volete specificare ulteriori istruzioni in merito alla visibilità (public, private, protected), potete omettere del tutto la keyword.
class Tardis (var year: Int, var place: String)
Tutti e tre gli esempi di codice restituiscono lo stesso risultato.
Potete adesso inserire la classe nel resto del vostro testo sorgente e alimentarla con valori concreti.
Potete adesso inserire la classe nel resto del vostro testo sorgente e alimentarla con valori concreti.
val tardis1 = Tardis(2133, "Dunlop Station")
val tardis2 = Tardis(1885, "Northhampton")
Come per la maggior parte dei linguaggi a oggetti potete accedere alle proprietà e ai metodi di un oggetto posizionando un punto e il nome della proprietà o del metodo dopo quello dell'oggetto stesso.
class Tardis (var year: Int, var place: String)
val tardis1 = Tardis(2133, "Dunlop Station")
val tardis2 = Tardis(1885, "Northhampton")
fun main() {
println(tardis1.year)
}
Una particolarità di Kotlin sono le data class. Questo tipo di classe ha l'unico scopo di immagazzinare dati. A tale scopo è, in genere, sufficiente una riga di codice.
data class User (var username: String, var name: String, var age: Int)
Questa classe può essere utilizzata direttamente.
data class User (var username: String, var name: String, var age: Int)
fun main() {
val user1 = User ("River Song", "Melody Pond", 200)
println("Username: " + user1.username)
println("Name: " + user1.name)
println("Age: " + user1.age)
}
Oggetti (objects)
In Kotlin gli oggetti sono istanze che possono essere definite in un determinato modo una sola volta (singleton). In genere contengono variabili e funzioni. Per creare un oggetto è solitamente sufficiente una sola riga di codice, come anche per le classi. L'oggetto creato sarà comunque vuoto e i relativi contenuti saranno aggiunti al suo corpo.
object myObject {
fun sum(a: Int, b: Int): Int {
return a+b
}
}
Cicli (loop)
In Kotlin potete scegliere tra tre diverse tipologie di ciclo: while, do..while e if, che si comportano esattamente come le loro controparti in altri linguaggi di programmazione. Un ciclo di tipo while viene eseguito finché una determinata condizione resta verificata.
fun main() {
var n = 1
while (n <= 10) {
println(n++)
}
}
Il ciclo do..while si comporta in modo analogo a quello con while. La differenza sta nel fatto che in questo caso il ciclo viene eseguito almeno una volta, dato che il controllo della condizione viene eseguito soltanto al termine del ciclo.
fun main() {
var n = 1
do {
n++
}
while (n < 1)
println(n)
}
Il ciclo for viene eseguito finché la condizione è vera.
val myRange = 0..10
fun main() {
for (n in myRange) {
print("$n ")
}
}
Condizioni (conditions)
Kotlin adotta tre diverse possibilità per eseguire istruzioni o diramazioni condizionali: if, if..else e when. Mediante l'espressione if, il computer esegue un'istruzione se la condizione è vera.
val whoCompanion = arrayOf("Bill Potts", "Clara Oswald", "Amy Pond", "Martha Jones", "Donna Noble", "Rose Tyler")
fun main() {
if ("Rose Tyler" in whoCompanion) {
print("yes")
}
}
Con else aggiungete un'azione che deve essere eseguita qualora la condizione non fosse vera.
val whoCompanions9 = arrayOf("Rose Tyler")
val whoCompanions10 = arrayOf("Martha Jones", "Donna Noble", "Rose Tyler")
val whoCompanions11 = arrayOf("Clara Oswald", "Amy Pond")
val whoCompanions12 = arrayOf("Bill Potts", "Clara Oswald")
fun main() {
var whoCompanion = "Clara Oswald"
if (whoCompanion in whoCompanions9) {
print("yes")
}
else {
print("no")
}
}
Infine l'espressione when è una particolarità di Kotlin: a seconda delle condizioni vengono eseguite azioni diverse. L'espressione when somiglia quindi a quello che in altri linguaggi di programmazione viene definito switch, ma lavora in modo più preciso.
Nel corpo di when inserite i diversi casi di controllo, che sono sempre collegati a una determinata variabile.
Nel corpo di when inserite i diversi casi di controllo, che sono sempre collegati a una determinata variabile.
var age = 17
fun main() {
when {
age > 18 -> println("Sei troppo grande!")
age == 18 -> println("Già adulta!")
age == 17 -> println("Benvenuta!")
age <= 16 -> println("Sei troppo giovane!")
}
}
Se l'argomento viene trasferito direttamente a when non è necessario ripeterlo ogni volta nel corpo. Una singola condizione può inoltre eseguire diverse azioni. A tale scopo, create mediante parentesi graffe un nuovo corpo e per evitare casi inaspettati utilizzate else.
fun multi(a: Int, b: Int, c: Int): Int {
return a*b*c
}
fun main() {
val d = "yes"
when (d) {
"no" -> println("Nessun calcolo")
"yes" -> {
println("Avvia calcolo")
println(multi(5, 2, 100))
println("Calcolo terminato")
}
else -> println("Inserimento errato")
}
}
Nullabilità
Un aspetto molto scoraggiante nella programmazione con Java è l'errore NullPointerException, che si presenta quando si chiama un oggetto con valore null. Kotlin aggira questo problema impedendo di default alle variabili di assumere il valore null. Se si verificasse questa condizione, comparirebbe già durante la compilazione la seguente nota: "Null can not be a value of a non-null type String" o un avviso simile.
Ci sono però momenti in cui si intende utilizzare il valore null intenzionalmente. Per questi casi, Kotlin utilizza l'operatore di chiamata sicura ?.
Ci sono però momenti in cui si intende utilizzare il valore null intenzionalmente. Per questi casi, Kotlin utilizza l'operatore di chiamata sicura ?.
fun main() {
var password: String? = null
print(password)
}
In questo modo indicate esplicitamente a Kotlin che il valore null è ammissibile. Il programma restituisce null. Se invece volete indirizzare una proprietà particolare della variabile, dovete nuovamente ricorrere all'operatore di chiamata sicura.
fun main() {
var password: String? = null
print(password?.length)
}
Anche questo codice restituirà null, ma senza produrre errori o compromettere il funzionamento del programma. Un modo più elegante sarebbe indicare un valore alternativo. A tale scopo potete usare l'operatore elvis (?:), così chiamato per la sua somiglianza a una faccina sorridente con il ciuffo.
fun main() {
val firstName = null
val lastName = "Pond"
val name: String = firstName?: "Nome mancante" + " " + lastName?: "Cognome mancante"
print(name)
}
In questo esempio vengono visualizzate delle note nel caso in cui una variabile abbia il valore null.