Kotlin Android

Introduzione

Kotlin è un linguaggio moderno perfetto per lo sviluppo di applicazioni Android che non introduce alcuna restrizione

Alcune caratteristiche dell’uso di Kotlin per lo sviluppo delle app Android:

  • Compatibilità: Kotlin è completamente compatibile con JDK 6, garantendo che le applicazioni Kotlin possano funzionare su dispositivi Android precedenti senza problemi. Gli strumenti di Kotlin sono pienamente supportati in Android Studio e compatibili con il sistema di build Android.

  • Prestazioni: un’applicazione Kotlin è veloce quanto una Java equivalente, grazie a una struttura bytecode molto simile. Con il supporto di Kotlin per le funzioni inline, il codice che utilizza lambda spesso viene eseguito anche più velocemente dello stesso codice scritto in Java.

  • Interoperabilità: Kotlin è al 100% interoperabile con Java, consentendo di utilizzare tutte le librerie Android esistenti in un’applicazione Kotlin. Ciò include l’elaborazione delle annotazioni, quindi anche il databinding e il Dagger funzionano.

  • Impatto: Kotlin ha una libreria runtime molto compatta, che può essere ulteriormente ridotta con l’uso di ProGuard. In un’applicazione reale, il runtime di Kotlin aggiunge solo poche centinaia di metodi e meno di 100K alla dimensione del file .apk.

  • Tempo di compilazione: Kotlin supporta ed è ottimizzato per la compilazione incrementale, quindi, per le build pulite c’è un sovraccarico, le build incrementali sono solitamente veloci o più veloci rispetto a Java.

  • Curva di apprendimento: per uno sviluppatore Java, iniziare a usare Kotlin è molto semplice. Il convertitore automatico da Java a Kotlin incluso nel plugin Kotlin aiuta con i primi passi. Kotlin Koans offre una guida attraverso le caratteristiche principali della lingua con una serie di esercizi interattivi.

Strumenti per lo sviluppo Android

Il team di Kotlin offre un set di strumenti per lo sviluppo Android che va oltre le funzionalità linguistiche standard:

  • Kotlin Android Extensions è un’estensione per compilatore che ti consente di sbarazzarti delle chiamate findViewById () nel tuo codice e di sostituirle con proprietà sintetiche generate dal compilatore.

  • Anko è una libreria che fornisce una serie di wrapper compatibili con Kotlin attorno alle API Android, oltre a una DSL che consente di sostituire i file .xml del layout con il codice Kotlin.

Sintassi di base

Definizione package

Il package va definito all’inizio del file:

package my.demo

import java.util.*

// ...

Definizione funzioni

Funzione con passaggio di due parametri di tipo Int che ritornerà un Int:

fun sum(a: Int, b: Int): Int {
    return a + b
}

Funzione con nessun valore di ritorno (void di Java):

fun printSum(a: Int, b: Int): Unit {
    println("sum of $a and $b is ${a + b}")
}

volendo :Unit si può omettere

Definizione variabili

Le variabili di sola lettura il cui valore può essere assegnato una sola volta vengono dichiarate con la chiave val:

val a: Int = 1  // assegnazione immediata
val b = 2   // assegnazione con tipologia deducibile in questo caso `Int`
val c: Int  // Indicare la tipologia se la variabili non viene inizializzata
c = 3       // assegnazione di valore dopo la dichiarazione

Le variabili, invece, il cui valore può essere riassegnato vengono dichiarate con la chiave var:

var x = 5 // tipo `Int` deducibile
x += 1

Commenti

I commenti in Kotlin sono come quelli in Java, supportano sia il commento della singola riga che a blocco.

A differenza di Java, i commenti di blocco in Kotlin possono essere annidati.

 // Commento riga singola
 /* Commento in blocco
di più righe */

 /*
 Commento in blocco
     /*
     Commento in blocco annidato
     */
 */

Utilizzo delle stringhe

var a = 1
// aggiunta del nome `a` nel modello `s1`:
val s1 = "a is $a"

a = 2
// espressione complessa in un modello :
val s2 = "${s1.replace("is", "was")}, but now is $a"

Utilizzo delle espressioni condizionali

fun maxOf(a: Int, b: Int): Int {
  if (a > b) {
      return a
  } else {
      return b
  }
}

//L'uso dell'if in una espressione
fun maxOf(a: Int, b: Int) = if (a > b) a else b

Utilizzo dei valori nulli e controllo per null

Bisogna esplicitamente indicare se un valore può ritornare null.

La funziona ritornerà null se non sarà un valore intero:

fun parseInt(str: String): Int? {
    // ...
}

fun printProduct(arg1: String, arg2: String) {
    val x = parseInt(arg1)
    val y = parseInt(arg2)

    // L'uso di `x * y` restituisce un errore perchè potrebbe contenere valori nulli.
    if (x != null && y != null) {
        // x e y vengono automaticamente convetiti in valore non nulli
        println(x * y)
    }
    else {
        println("'$arg1' o '$arg2' non sono numeri")
    }
}

Utilizzo dei controlli sul tipo e i cast automatici

L’operatore is controlla se l’espressione è un’istanza di un tipo. Se una variabile viene controllata per un tipo specifico, non è necessario eseguire il cast esplicito:

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // `obj` viene automaticamente tipizzato `String` dopo questo controllo
        return obj.length
    }

    // `obj` è ancora di tipo `Any` fuori dal controllo
    return null
}

o

fun getStringLength(obj: Any): Int? {
    if (obj !is String) return null

    // `obj` è automaticamente tipizzato `String` dopo il controllo
    return obj.length
}

o ancora

fun getStringLength(obj: Any): Int? {
    // `obj` viene automaticamente tipizzato a `String` dopo `&&`
    if (obj is String && obj.length > 0) {
        return obj.length
    }

    return null
}

Utilizzo dei cicli

Ciclo for:

val items = listOf("apple", "banana", "kiwifruit")
for (item in items) {
    println(item)
}

or

val items = listOf("apple", "banana", "kiwifruit")
for (index in items.indices) {
    println("item at $index is ${items[index]}")
}

Ciclo while:

val items = listOf("apple", "banana", "kiwifruit")
var index = 0
while (index < items.size) {
    println("item at $index is ${items[index]}")
    index++
}

Utilizzo dell’espressioni when

Le espressioni when sono come degli swith ma più potenti:

fun describe(obj: Any): String =
when (obj) {
    1          -> "One"
    "Hello"    -> "Greeting"
    is Long    -> "Long"
    !is String -> "Not a string"
    else       -> "Unknown"
}

Utilizzo degli intervalli

Per controllare se un numero appartiene o meno a un intervallo si può utilizzare l’operatore in:

val x = 10
val y = 9
//controllo del valore x se contenuto nell'intervallo da 1 a y+1 (in questo caso 10)
if (x in 1..y+1) {
    println("fits in range")
}

//ex. out of range

val list = listOf("a", "b", "c")

if (-1 !in 0..list.lastIndex) {
    println("-1 is out of range")
}
if (list.size !in list.indices) {
    println("list size is out of valid list indices range, too")
}

//ciclo su un intervallo

for (x in 1..5) {
    print(x)
}

//circlo su un intervallo con pregressione

for (x in 1..10 step 2) {
    print(x)
}
println()
for (x in 9 downTo 0 step 3) {
    print(x)
}

Utilizzo delle collezioni

Cicli su delle collezioni

for (item in items) {
  println(item)
}

//controlla se la collezione contiene gli oggetti

when {
    "orange" in items -> println("juicy")
    "apple" in items -> println("apple is fine too")
}

//utilizzo dell'espressione lambda per filtrare e mappare la collezione:

val fruits = listOf("banana", "avocado", "apple", "kiwifruit")
fruits
  .filter { it.startsWith("a") }
  .sortedBy { it }
  .map { it.toUpperCase() }
  .forEach { println(it) }

Creazioni di classi base e le loro istanze

val rectangle = Rectangle(5.0, 2.0) //non è necessaria la chiave 'new'
val triangle = Triangle(3.0, 4.0, 5.0)