Perché Kotlin è il miglior linguaggio per sviluppare su Android

Kotlin è il primo linguaggio per Android ufficialmente supportato da Google in aggiunta a Java stesso. Questo da solo è un traguardo significativo e un indizio di quanto Kotlin sia un un buon linguaggio per Android. Un altro buon segno è .

In questo articolo spiegheremo le ragioni per cui Kotlin è particolarmente adatto allo sviluppo su Android, concentrandoci su due differenti aspetti:

  1. descriveremo le caratteristiche di Kotlin che sono particolarmente rilevanti per lo sviluppo su Android
  2. guarderemo all’ecosistema che circonda il linguaggio stesso: cose come l’IDE e la JVM su Android

Tenendo conto di tutto questo c’è solo una possibile ragione per non usare Kotlin nei tuoi nuovi progetti Android: non conosci il linguaggio. Ma non preoccuparti, anche in questo possiamo suggerirti alcune risorse per imparare Kotlin.

Caratteristiche del linguaggio e librerie

Le applicazioni per Android hanno alcune caratteristiche specifiche comuni, tipiche di applicazioni con un’interfaccia grafica. Per esempio, fanno un frequente uso di chiamate asincrone. Kotlin offre un buon supporto per questo uso e ha anche una o due librerie che possono aiutare quando il linguaggio stesso non può.

Profonda interoperabilità

In un articolo che confronta Scala con Kotlin (in inglese) puoi vedere come l’elevato grado di interoperabilità tra Java e Kotlin permette una transizione senza intoppi tra i due, o persino una coesistenza pacifica e produttiva. Questa interoperabilità coinvolge tutti gli aspetti: dal linguaggio stesso al bytecode prodotto. Il che è fondamentale per permettere il riuso di importanti librarie che analizzano quest’ultimo.

Se sei interessato a informazioni specifiche sul linguaggio puoi trovare nella documentazione Kotlin una lista completa di caratteristiche per il supporto a Java. Questa interoperabilità è profonda e intelligente. Per esempio, Kotlin supporta anche alcune convenzioni dello sviluppo in Java, come nel seguente esempio basato su getters e setters, dalla documentazione.

import java.util.Calendar

fun calendarDemo() {
    val calendar = Calendar.getInstance()
    if (calendar.firstDayOfWeek == Calendar.SUNDAY) {  // chiama getFirstDayOfWeek()
        calendar.firstDayOfWeek = Calendar.MONDAY       // chiama setFirstDayOfWeek()
    }
}

Questa è un’ottima notizia per lo sviluppo su Android, perché gli SDK sono ovviamente ancora creati per Java. Così, grazie ad una grande interoperabilità, puoi comunque scrivere del codice Kotlin produttivo, anche se devi usare librerie Java.

Argomenti delle funzioni

In Kotlin gli argomenti delle funzioni possono avere nomi e valori predefiniti. Questo semplifica la lettura e la comprensione nel punto di chiamata e permette di limitare il numero di overload delle funzioni. Ciò perché non devi definire una nuova funzione per ogni argomento opzionale, devi solo inserire un valore predefinito nella definizione.

Confrontiamo le definizioni in Java e Kotlin.

 // Java
void drawText(int x, int y, int size, int spacing, String text)
{ [..] }

// Kotlin
fun drawText(x: Int = 0, y: Int = 0, size: Int = 20, spacing: Int = 0, String: text)
{ [..] }

E chiamando la stessa funzione in Java e Kotlin.

// Java
drawText(50, 200, 20, "hello");

// Kotlin
// using default values
drawText("kneel in front of the Kotlin master!")

// using named arguments
drawText(10, 25, size = 20, spacing = 5, "hello")

Tipi nullable

In Kotlin devi impostare esplicitamente che una variabile possa avere il valore nullo, perché normalmente non può. Ciò consente di evitare il comune e temuto NullPointerException durante l’esecuzione, perché la maggior parte degli errori sono individuati dal compilatore al momento della compilazione. I riferimenti a null sono così rischiosi che il creatore del primo linguaggio con riferimenti a null li ha chiamati l’errore da un miliardo di dollari.

var text: String = null // non compila
var unsafeText: String? = null // ok

Kotlin tiene conto della possibilità di avere valori null o, all’opposto, la sicurezza dei tipi a tutti i livelli. Per esempio, è tenuto in considerazione durante i confronti.

unsafeText.length // non compila

if (unsafeText != null) {
  unsafeText.length // funziona, ma non è il modo migliore
}

Dopo aver controllato che un tipo nullable sia, al momento, non null puoi usare la variabile come se fosse non nullable, perché dentro il blocco è certo che il suo valore sia valido. Ovviamente questo è anche prolisso, ma c’è un modo migliore, quello suggerito, che è equivalente, ma più conciso.

unsafeText?.length // funziona

Il safe call operator (?.) garantisce che si acceda alla variabile solo nel caso in cui unsafeText sia non null.

I tipi nullable sono particolarmente utili in Android perché null è largamente usato. Questo può portare a molti bug, ma in Java non ci sono alternative. Persino guardando le API ufficiali puoi vedere annotazioni @NotNull un po’ da tutte le parti.

Infatti null è usato per varie ragioni, dalla performance al ciclo di vita dell’applicazione, che potrebbe richiedere che certe azioni siano eseguite prima che tu possa inizializzare nel modo appropriato una variabile. In simili casi potresti pensare che non puoi evitare di usare null, ma avresti torto. Ci sono diversi modi di farlo, inclusi late initialization o lazy initialization.

Lazy e Late Initialization

La late initialization funziona solo per variabili dentro il corpo di una classe. La variabile non può essere di un tipo nullable. Devi aggiungere il modificatore lateinit e quindi dentro un metodo inizializzare la variabile. Se accedi alla variabile prima di averla inizializzata viene lanciata un’eccezione speciale, che indica chiaramente che cosa hai sbagliato. Il modificatore lateinit è stato introdotto per la dependency injection, ma può essere utile anche in altri scenari.

Se necessiti di un valore costante, invece, puoi usare la funzione lazy() per delegare l’inizializzazione ad una funzione. Lazy accetta una lambda che viene eseguita la prima volta che si accede alla variabile. La funzione inizializza la variabile e restituisce il suo valore. Agli accessi successivi la funzione non è eseguita, ma il valore della variabile viene restituito direttamente.

val computedValue: String by lazy {
    println("I am executed only the first time!")
    "A Value That Will Be Subsequently Remembered"
}

Lazy è utile quando la computazione del valore è costosa ed il valore stesso non cambia.

Le Data Class

Se vuoi raggruppare valori semanticamente collegati devi usare una classe. In una app Android sono frequentemente usate per rappresentare il modello dell’applicazione. La classe deve semplicemente contenere i valori, ma solitamente hai bisogno di alcuni metodi standard per gestire i dati, ottenere e impostare valori delle proprietà, ecc. E devi anche aggiornare questi metodi ogni volta che cambi la struttura della classe.

Kotlin ti dà tutto quello di cui hai bisogno automaticamente, semplicemente usando la keyword data prima della definizione della classe.

data class User(val name: String, var password: String, val age: Int)

Tutto qui. Ora hai disponibili:

  • getters e setters (questi ultimi solo per riferimenti a variabili) per leggere e scrivere tutte le proprietà
  • component1() .. componentN() per tutte le proprietà nel loro ordine di dichiarazione. Sono usate per le destructuring declarations.
  • equals(), hashCode() and copy() per gestire gli oggetti della classe (cioé confrontarli e copiarli)
  • toString() per scrivere un oggetto nella forma umanamente leggibileName_of_the_class(Name_of_the_variable=Value_of_the_variable, [..])"

Nota che getters e setters sono automaticamente definiti per ogni classe in Kotlin, non solo per le data class. Ma, ovviamente, in Java dei definirli tu.

Per esempio, data la precedente classe User

val john = User("john","secret!Shhh!", 20)
 
println(john.component1()) // scrive su schermo "john"
// principalmente usate automagicamente nelle destructuring declaration come questa
val (name, password, age) = john
println(age) // scrive su schermo 20

println(john) // scrive su schermo User(name=john, password=secret!Shhh!, age=20)"

È strabiliante? No.

È una cosa molto utile? Si, lo è. Aiuta a risparmiare tempo e permette più rapidi cambiamenti al modello di un’applicazione Android, dato che non devi scrivere tu stesso funzioni specifiche per confrontare gli oggetti.

String Template

Un’altra caratteristica di Kotlin che ne aumenta l’espressività sono gli string template. Essi sono delle espressioni che vengono valutate e poi il loro valore viene inserito nella stringa in cui sono definite. Per definire un’espressione dentro una stringa di testo devi semplicemente farla precedere dal simbolo del dollaro ($).

val number = 25
// text ora è "25 è un numero grande per me"
val text = "$number è un numero grande per me"

Nel caso ti serva un’espressione più complessa puoi racchiuderla dentro delle parentesi graffe.

val number = 25
// text ora è "26 è un numero grande per me"
val text = "${number + 1} è un numero grande per me"

Puoi usare gli string template ovunque possa usare una stringa, sia le stringhe normali, cioè gli string literal, che quelle che non supportano sequenze di escape e vengono usate per lunghi blocchi di testo su più linee, cioè le raw string.

val number = 25
// text ora è "$25 \n sono tanti soldi"
val text = """${'$'}${number}:
sono tanti soldi"""

Dato che le raw string non supportano sequenze di escape se devi usare il simbolo del dollaro in una raw string devi usare una template expression corrispondente al carattere del dollaro.

E con questo ti risparmi una lunga serie di noiose e rischiose "testo" + espressione e ottiene un codice più pulito.

Eliminare le utility class

Le applicazioni Android hanno bisogno di utility class per aggiungere nuove funzioni a classi esistenti o per compiere ripetute operazioni specifiche per la propria app. Kotlin offre un paio di modi per eliminarle: extension method e la possibilità di usare funzioni globali senza definire una classe.

È importante ricordare che entrambi sono quello che viene definito come syntactic sugar. Ovvero semplificazioni che esistono solo per l’utente e che il compilatore trasforma nella forma appropriata.

Usando un extension method per estendere una classe non puoi accedere a membri privati o protetti della classe.

// extension method
fun String.bePolite() : String {
  // string templating con $
  return "$this, please"
}

val ask = "Pass me the salt"
println(ask.bePolite()) // scrive su schermo "Pass me the salt, please"

E se vuoi usare le funzioni globali in Java devi ricordare che sono comunque generate dentro una classe. Il nome della classe generata è basato sul nome del file in qui l’extension method è stato definito, ma questo può essere cambiato con un’annotazione.

// nome del file polite.kt
package strings

fun checkPolite(request: String) : Boolean {
    if (request.contains("please"))
        return true
    else
        return false
}

// un file Java
import strings.PoliteKt;

PoliteKt.checkPolite("prove P=NP, please");

C’è anche un’altra caratteristica che rende gli utility method più utili: operator overloading. Non puoi definire operatori personalizzati, ma puoi usare molto facilmente gli operatori standard con le tue classi. Tutto quello che devi fare è definire una funzione con un nome specifico e Kotlin trasformerà l’operatore corrispondente in essa nel punto di chiamata. Per esempio, se definisci una funzione plus() per la tua classe, Kotlin trasformerà objectOfYourClass1 + objectOfYourClass2 in objectOfYourClass1.plus(objectOfYourClass2).

Caratteristiche funzionali

La programmazione funzionale non è nuovo paradigma, ma il suo successo è relativamente recente. Ciò è dovuto all’aumento delle situazioni che richiedono molta concorrenza e parallelismo. Le applicazioni Android non sono il miglior esempio d’uso per applicazioni completamente funzionali, ma hanno bisogno di chiamate asincrone per interazioni con l’interfaccia utente e spesso per chiamare servizi online backend, pertanto possono beneficiare di caratteristiche funzionali. In breve, le applicazioni Android traggono beneficio dalle caratteristiche funzionali nel contesto della programmazione imperativa.

La caratteristiche più comunemente usate sono le funzioni di ordine superiore e le lambda. Le funzioni di ordine superiore sono funzioni che accettano una funzione come argomento o ne restituiscono una. Le lambda sono funzioni anonime che possono essere passate in giro. Il problema è che solo con la versione 8 Java ha cominciato ad includerle, pertanto molti sono costretti ad usare una libreria, come Retrolambda. La ragione per cui non si può usare Java 8 è spiegata nel paragrafo successivo The Java Platform On Android. Questa situazione è ben lontana dall’ideale, perché il linguaggio stesso non le supporta e quindi molte librerie non possono trarne vantaggio.

Un linguaggio come Kotlin, che le include dall’inizio, permette di trarne vantaggio senza limitazioni.

Riteniamo che il lettore conosca già il vantaggio di simili caratteristiche funzionali, quindi mostreremo un esempio di quanto sia produttivo e semplice usarle in Kotlin, grazie alle sue molte convenzioni.

val strings: List = listOf("one", "two", "three", "Hello", "to", "Kotlin", "and", "Me")
// innanzitutto prendiamo solo le stringe più lunghe di 3 
// poi trasformiamo ogni stringa nella sua versione a caratteri minuscoli
// infine le uniamo in un'unica stringa
println(strings.filter { it.length > 3 }.map { it.toLowerCase() }.joinToString())
// scrive su schermo "three, hello, kotlin"

Se hai familiarità con C# avrai notato quanto sia simile a LINQ. Ciò è anche osservato nella documentazione ufficiale. Ed è una dimostrazione di quanto sia pragmatico Kotlin: ha preso il meglio di molti linguaggi e ci ha aggiunto del suo. In questo caso il suo è il nome predefinito it, che può essere usato quando la lambda accetta un solo argomento. Sua è anche la possibilità di inserire la lambda fuori dalle parentesi, se essa è l’ultimo argomento, e di omettere del tutto le parentesi, se essa è l’unico argomento (es. per filter e map).

Conciso, leggibile e facile da usare; cosa puoi chiedere di più?

Estensioni Kotlin per Android

Ci sono anche delle estensioni Kotlin per Android che permettono, tra le altre cose, di rimpiazzare le irritanti e foriere di bug chiamate a findViewById() con proprietà sintetiche. Un esempio di chiarimento, proveniente dalla documentazione:

// Usando R.layout.activity_main dai sorgenti principali
import kotlinx.android.synthetic.main.activity_main.*

class MyActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        textView.setText("Hello, world!") // Invece di findViewById(R.id.textView) as TextView
    }
}

Ci sono alcune librerie Java che permettono di fare qualcosa di simile, ma solitamente richiedono annotazioni. Kotlin offre un modo più semplice, senza codice aggiuntivo o runtime.

Anko

Anko è una libreria Kotlin che rende lo sviluppo di applicazioni Android più veloce e più semplice. Rende il tuo codice pulito e facile da leggere, ti permette di dimenticarti delle parti più spigolose del SDK Android per Java.

Anko è una popolare libreria che semplifica lo sviluppo di applicazioni Android con molti helper. Infatti sono troppi per menzionarli tutti qui, ma possiamo dire che la libreria è divisa in diverse parti:

  • Anko Commons: piena di helper per intent, dialog e toast, logging, risorse e dimensioni…
  • Anko Layouts: un modo veloce e type-safe per scrivere layout dinamici Android
  • Anko SQLite: un componente con una collezione di supporto a query e parsing per Android SQLite

Mostreremo un esempio di Anko Layouts perché mostra anche un’utile caratteristica di Kotlin: type-safe builders. Questi sono un’alternativa più sicura e concise ai file di configurazioni e cose del genere, usati per descrive dati con una struttura gerarchica. Il tipico esempio è un file XML usato per descrivere una UI.

Usare un file XML richiede di riscrivere ogni volta le stesse parti di supporto alla definizione principale. L’unica cosa che cambia sono i dati stessi. Non solo è noioso, ma anche foriero di errori, difficile da leggere e non sicuro. Questo perché è facile commettere un errore. I builder in Kotlin forniscono un’alternativa migliore.

Puoi trasformare il seguente codice Kotlin (e non parliamo neanche del corrispondente codice Java…)

val act = this
val layout = LinearLayout(act)
layout.orientation = LinearLayout.VERTICAL
val name = EditText(act)
val button = Button(act)
button.text = "Say Hello"
button.setOnClickListener {
    Toast.makeText(act, "Hello, ${name.text}!", Toast.LENGTH_SHORT).show()
}
layout.addView(name)
layout.addView(button)

…nella seguente versione decisamente migliore…

verticalLayout {
    val name = editText()
    button("Say Hello") {
        onClick { toast("Hello, ${name.text}!") }
    }
}

Che crea la seguente interfaccia.

Layout di esempio definito con Anko

Layout di esempio dalla documentazione Anko

C’è persino un plugin per Android Studio/Intellij IDEA per vedere in anteprima le interfacce create con Anko.

Creare applicazioni Kotlin per Android

Ci sono alcuni aspetti tecnici da considerare quando si creano applicazioni Kotlin per Android. Esse sono relative sia a Java che agli strumenti usati per sviluppare per Android e coinvolgono diversi interessi degli sviluppatori oltre al linguaggio stesso.

La piattaforma Java su Android

Tabella e grafico rappresentanti le percentuali di dispositivi per ogni differente versione di Android

Dati sulle differenti versioni Android dalla documentazione ufficiale

Gli sviluppatori esperti di Android sapranno già che la storia di Java su Android è complicata. Per gli altri ecco una breve sintesi. Quando Android è stato lanciato non c’era una versione open-source, stabile e matura di Java, dato che OpenJDK era appena agli inizi. Pertanto Android era basato su di una versione modificata di Apache Harmony. Ma con la maturazione di OpenJDK il supporto delle aziende, come IBM, per Apache Harmony è diminuito.

Apache Harmony supportava il 97% di Java 6, ma il progetto finì nel 2010, lasciando Google a supportare l’intero codice, sia la sua parte personalizzata che la fondazione open-source. Inoltre Oracle fece causa a Google anche perché sosteneva che Android fratturasse la piattaforma Java. Pertanto Google ha cambiato la base di Android da Apache Harmony a OpenJDK con Android 7, il che ha anche dato ad Android una base di più alta qualità.

Ciò significa che, al momento, la maggior parte dei dispositivi Android supporta Java 6, mentre l’ultima versione di Android, e quelle future, dovrebbero supportare l’ultima versione di Java. Tuttavia c’è ancora il problema che esiste un ritardo tra la versione di Java supportata da OpenJDK e quella supportata da Android, dato che Google deve adattare OpenJDK alle sue parti personalizzate.

Ciò aggiunge altro tempo a quello che è già un ciclo di rilascio relativamente lento, come quello di Java, perlomeno negli ultimi anni. Kotlin invece è subito disponibile per Android, non appena una nuova versione è rilasciata da JetBrains.

Dato che il compilatore Kotlin produce bytecode compatibile con Java 6, Kotlin può portare in tutta sicurezza le sue caratteristiche alla maggioranza dei dispositivi Android usati in questo momento. Pertanto è l’unica scelta praticabile per la maggioranza degli sviluppatori Android che vogliono avere un linguaggio di programmazione moderno e produttivo.

Supporto IDE e dei vari strumenti

L’IDE ufficialmente supportato da Google per sviluppare applicazioni Android è Android Studio. Questo è l’IDE che ha rimpiazzato il precedente basato su Eclipse ed è basato sulla versione open-source (community) di IntelliJ IDEA. IntelliJ IDEA è sviluppato da JetBrains stessa e ovviamente supporta Kotlin. Ma Android Studio supporterà nativamente Kotlin dalla versione 3, al momento in pre-release. C’è anche plugin Kotlin per le precedenti versioni di Android Studio.

Questo è parte del supporto ufficiale di Kotlin per lo sviluppo su Android, ma non è l’unica cosa. Abbiamo già visto alcune librerie ed estensioni create da JetBrains per Android.

Android Studio usa Gradle per la build automation e Kotlin può essere usato come linguaggio per lo scripting Gradle.

Infine, lo SDK Android supporta ProGuard, uno strumento per ottimizzare, rimuovere metodi non usati e offuscare il bytecode Java. Data la similarità tra il bytecode Kotlin e quello Java bytecode ProGuard funziona bene anche per Kotlin. Questo è un buon esempio di quanto in profondità vada lo sforzo di JetBrains per mantenere compatibilità tra Java e Kotlin e anche il motivo per cui lo fanno.

Limitazioni APK

A parte il supporto a Java ci sono molte piccole differenze fra la JVM ufficiale e quella supportata da Android. Una particolarmente importante per lo sviluppo su Android è il limite sul numero dei metodi e campi in un’applicazione: 216, il che significa un po’ più di 65000. La Kotlin runtime e la libreria standard library insieme aggiungono circa 7 000 metodi, che è circa il 10% del limite dei metodi. Questo non è un contributo insignificante, ma usando ProGuard può essere notevolmente ridotto, in alcuni casi persino a 42!

Inoltre Kotlin può effettivamente essere un vantaggio in tal senso se ti permette di rimuovere una librerie alternativa. Buoni candidati sono librerie che introducono poche caratteristiche funzionali o migliorano la produttività o qualità della vita di sviluppatore. Gli sviluppatori di sono riusciti a ridurre il numero totali di metodi del 10% e il totale delle linee di codice del 30% con la conversione da Java. Un altro sviluppatore di app ha raggiunto una riduzione del 20% delle linee di codice.

Inoltre, per dare la corretta prospettiva alle cose, 7000 metodi non sono così tanti se confrontati con quelli di una libreria come Google Guava, che ne ha quasi 15 000. E non parliamo neanche dei numeri di altri linguaggi, come i 50 000 di Scala o i 30 000 di Groovy. Numeri così alti li rendono praticamente impossibili da usare per sviluppare su Android, quale che ne sarebbero i vantaggi.

C’è anche un limite sulle dimensioni del APK che si può caricare sul Google Play Store. Questo limite è di 100MB. Anche senza questo limite artificiale ci sarebbe comunque da considerare lo spazio limitato sul dispositivo dell’utente e potenzialmente i costi di trasferimento del APK attraverso connessioni non flat. Pertanto il fatto che Kotlin aggiunga solo circa 1MB per un debug APK, e ancor meno per uno release, è una buona cosa.

In sintesi: usare Kotlin ha delle conseguenze in luce delle limitazioni APK, ma considerando le alternative è una scelta ovvia.

Strumento di conversione da Java

JetBrains ha fatto un buon lavoro con il suo plugin Kotlin e la sua capacità di auto-convertire Java in Kotlin. Può trasformare correttamente il 60–70% della tua classe, lasciandoti con qualche aggiustamento e cose idiomatiche/stilistiche da sistemare.

How we made Basecamp 3’s Android app 100% Kotlin

JetBrains ha sviluppato uno strumento per convertire codice Java in Kotlin. A questo strumento si può accedere in molti molti, nell’ambiente online, che è utile per imparare come funziona, e negli IDE, come IntelliJ IDEA e Android Studio, che è utile per imparare come funziona e per usarlo nel lavoro.

È uno strumento molto utile, ma come tutti gli strumenti di conversione automatici non è perfetto. Studiare i risultati è necessario per comprenderne il funzionamento. Benché non dovrebbe rendere inutilizzabile la tua classe, non produce sempre il codice migliore. Pertanto è anche utile studiare i risultati per modificarli nel modo idiomatico e suggerito di usare Kotlin.

Velocità di compilazione

Il tempo necessario per compilare applicazioni Android è stato un punto dolente persino con Java per qualche tempo, dall’adozione di Gradle. Con Kotlin adesso la velocità è ottima, persino secondo le persone che durante la beta lo hanno criticato per la sua lentezza. È addirittura meglio di Java con Retrolambda.

Cosa significa ottimo? Per le clean build un’applicazione Kotlin è più lenta di una Java di circa il 15%. Questo rimane vero sia per la prima build che per le seguenti usando il demone Gradle. Ma dove conta per l’uso pratico, ovvero durante lo sviluppo, quando cambi pochi file alla volta prima di ricompilare (incremental build), è effettivamente più veloce di Java stesso.

Svantaggi

Il linguaggio Kotlin è già maturo, ma c’è ancora del lavoro da fare nell’ecosistema che sta attorno al linguaggio.

Supporto per le annotazioni

Qualche caratteristica avanzata di Java come il supporto per le annotazioni e reflection non era supportata ottimamente fino alle ultime versioni pre-release. Una caratteristica richiesta di frequente era il supporto per processare le annotazioni in Kotlin. In particolare molte persone volevano il supporto per ButterKnife, la famosa libreria per Android, che usa le annotazioni per il binding di metodi e campi con le view. C’erano già delle alternative per fare le stesse cose di ButterKnife con Kotlin, ma un buon supporto per le annotazioni era necessario.

Ora uno stabile supporto c’è, ma ci sono ancora delle difficoltà in alcuni casi, se non altro per la configurazione e la sostituzione di apt con kapt. Kapt è lo strumento per processare le annotazioni per Kotlin, che permette di supportare dagger, il veloce dependency injector per Android, e dbflow, un libreria android ORM per database che scrive il codice per il database al tuo posto. Al momento usare kapt rallenta notevolmente la velocità di compilazione.

L’ecosistema Kotlin è per la maggior parte pronto, ma ci sono anche dei problemi irritanti da risolvere.

Analisi del codice

Un modo in cui Kotlin è riuscito ad essere così ben compatibile con Java, e con così pochi costi aggiuntivi, è che molte delle cose intelligenti di Kotlin sono nel compilatore e non nel bytecode generato. Il che significa che la reflection è costosa e richiede una libreria specifica in aggiunta a quella runtime standard. Devi aggiungere kotlin-reflect che comporta quasi 20 000 metodi aggiuntivi alla tua app, il che è chiaramente ben lontano dall’ideale. Una soluzione alternativa è usare la reflection in Java o usare il supporto per la reflection Java in Kotlin. Per esempio, puoi usare String::class.java.methods per ottenere i metodi Java/JVM creati dal compilatore Kotlin.

La prima opzione ti costringe a continuare ad usare Java, almeno in alcune parti, ma sicuramente funziona in maniera prevedibile. La seconda ti permette di usare esclusivamente Kotlin, ma i risultati potrebbero essere differenti da quelli che ti aspetti. Questo perché la produttività garantita da Kotlin si basa su operazioni dietro le quinte. Per esempio, per permetterti di usare i valori predefiniti per le funzioni in Kotlin il compilatore genera un metodo Java per ogni combinazione di argomenti.

Un altro problema attuale è la mancanza di strumenti di static analysis per Kotlin. È vero che il compilatore Kotlin è più intelligente e sicuro di quello Java. Ed è anche che puoi effettivamente integrare il compilatore nel tuo workflow con la linea di comando. Però è innegabile che questo sia attualmente un punto debole di Kotlin, che probabilmente permarrà per qualche tempo, dato che il parser ufficiale di Kotlin è sì open-source, ma fortemente integrato nel compilatore. Quindi non è facile estrarlo e riutilizzarlo.

Kotlin è lo Swift di Android?

Kotlin and Swift logo

Kotlin e Swift hanno iniziato ad essere sviluppati all’incirca nello stesso periodo, ma Swift è stato rilasciato prima. Pertanto esiste una certa reputazione di Kotlin come lo Swift di Android. Questo non è tanto dovuto al fatto che uno si ispira all’altro, ma al fatto che entrambi sono stati sviluppati per portare un linguaggio di programmazione più moderno per i rispettivi sistemi operativi. Entrambi intendevano essere un miglioramento dei predecessori.

In questo senso i linguaggi hanno necessità simili di mantenere compatibilità. Come minimo hanno bisogno di mantenerlo al livello di design, dato che puntano agli stessi ambienti e sicuramente puntano agli stessi sviluppatori. Vi sono in gran parte riusciti. Ed essendo linguaggi sviluppati nello stesso periodo hanno anche simili interessi e caratteristiche, per esempio sono entrambi linguaggi nei quali l’inferenza dei tipi, nullability e valori costanti sono caratteristiche importanti.

Pertanto si potrebbe dire che Kotlin, perlomeno quando si parla di Android, è come Swift per iOS. Ciò è una buona cosa perché semplifica lo sviluppo e la comprensione. Potresti non avere lo stesso sviluppatore che lavora sia sulla versione Android che quella iOS della stessa app, ma avere linguaggi simili rende più semplice per entrambi i team coordinarsi e seguire una struttura simile. Questo aumenta la produttività e riduce i rischi di bug differenti introdotti in differenti versioni della stessa app.

Kotlin è migliore di Swift

Un’importante differenza, però, è che, come solitamente accade, gli strumenti e il design dei linguaggi Apple sono di una qualità leggermente inferiore rispetto alle alternative. Questo avviene perché Apple non ha vera competizione nel campo dello sviluppo. Se i tuoi clienti scelgono Apple tu, lo sviluppatore, non hai scelta, devi lavorare con quello che Apple ti dà.

Infatti una comune critica è l’instabilità di Xcode, se confrontata con quella di Android Studio. Inoltre lo sviluppo di Swift potrebbe essere stato troppo rapido: ci sono già state tre versioni di Swift in tre anni ed ogni versione ha introdotto dei breaking change. Benché sia meglio avere un linguaggio aggiornato che uno ammuffito, un linguaggio che si muove troppo in fretta non è ideale per la produttività di uno sviluppatore. JetBrains invece rilascia nuove versioni di Kotlin con una certa frequenza, ma senza introdurre dei breaking change.

Pertanto noi pensiamo che Kotlin per Android sia migliore di Swift per iOS: ha migliori strumenti di supporto ed è un linguaggio più stabile.

Riepilogo

Kotlin è un linguaggio progettato per essere semplice da usare ed estremamente produttivo. Porta nel mondo Java molte delle caratteristiche che mancavano e ne aggiunge di nuove che si adattano allo stile e ai bisogni degli sviluppatori Java esperti. Fa lo stesso per lo sviluppo su Android e in aggiunta a questo porta librerie e strumenti che semplificano tipici pattern di sviluppo per il sistema operativo mobile.

Non è un caso che Kotlin sia amato dagli sviluppatori Android e ufficialmente supportato da Google. Funziona alla grande ed è il miglior linguaggio di programmazione che puoi usare per Android oggi.

JetBrains ha iniziato concentrandosi nel supportare gli sviluppatori Android e ha ottenuto una gran popolarità tra di loro. Ora può usare questo successo per sfidare Java in altri campi. Ogni sviluppatore Android può ora beneficiare di questi investimenti e avvantaggiarsi dei risultati.

Se vuoi leggere questo articolo nell’originale inglese puoi leggerlo su Why Kotlin Is The Best Language For Android Development.

  • devmobile

    Un commento OT. Secondo te il recente ingaggio di jake Wharton in google può significare un tentativo di convertire il framework ed sdk di android verso Kotlin?

    • Gabriele Tomassetti

      Una domanda interessante. Non credo ci sarà una totale conversione del framework e SDK in Kotlin, semplicemente perché i vantaggi per gli sviluppatori Kotlin sarebbero minimi dato che l’interoperabilità è alta.

      Però, esattamente per lo stesso motivo, immagino che, con una maggiore adozione di Kotlin tra gli sviluppatori Android, Google finire implementerà alcune librerie primariamente in Kotlin. Quindi potrebbe fornire anche un SDK tutto in Kotlin, accanto ad uno in Java, che avrebbe parti che sono implementazioni complete in Kotlin e parti che sono semplici wrapper delle funzionalità del SDK Java.