insoddisfatta dalle limitazioni di Java, molte delle quali sono impossibili da risolvere per la necessità di mantenere la retro-compatibilità con il codice già presente. Com’è nato Kotlin?
era rendere Kotlin un linguaggio ad oggetti pronto all'uso professionale, migliore di Java e che permettesse alle aziende una graduale migrazione da Java. Com’è nato Kotlin?
principali (Google, Facebook, Amazon, etc.) è diventato linguaggio ufficiale per lo sviluppo su Android, con supporto di Google e dell’IDE ufficiale, Android Studio
ma varia l’ambiente. La strategia di Kotlin è principalmente fornire librerie e tool di supporto per • permettere agli sviluppatori che usano Kotlin di sfruttare le proprie competenze e i punti forti del linguaggio su più piattaforme • essere il più produttivi possibile su ogni piattaforma Su quali piattaforme si può usare Kotlin
si usa la keyword fun • Si possono dichiarare funzioni al livello più alto, non bisogna inserirle dentro una classe • I nomi degli argomenti vengono prima del tipo (come in Pascal) • In Kotlin gli array sono comuni classi e non hanno una sintassi speciale fun main(args: Array<String>) Hello world
schermo si usa la classica println, solo che Kotlin offre un wrapper che permette di evitare di scrivere System.out.println per intero. Stampare su schermo Println
: Int { return valore * 5 } Il tipo del valore restituito è indicato con due punti dopo gli argomenti della funzione. L’uso del punto e virgola dopo un’istruzione è opzionale. Parliamo di funzioni
un valore nell’intera lista int find(List<Integer> myList, value: Int) // cerca un valore in una lista a partire da un indice int find(List<Integer> myList, value: Int, from: Int) // scrive il numero in base 10 String toString(int value) // scrive il numero nella base specificata String toString(int value, int base) Parliamo di funzioni
1) find(myList, 10) find(myList, 7) // specifico il “from” usando la posizione find(myList, 1, 2) // specifico il “from” usando il nome find(myList, 1, from=2) Usiamo la funzione find appena dichiarata Parliamo di funzioni
mi da la possibilità di usare una scorciatoia: fun double(v: Int) = v * 2 fun numberOfExamsToGraduation() = Math.pow(2.0, 10.0) fun fullName(name: String, surname: String) = name + " " + surname Parliamo di funzioni
Se una classe non ha un corpo, si possono omettere le parentesi graffe class Vuota Una classe può avere un costruttore primario integrato nell’ header. Le proprietà dichiarate nel costruttore sono disponibili nel corpo della classe. Un costruttore si dichiara con la parola chiave constructor. class Persona constructor(nome: String) { } Classi
primario, se questo non ha modificatori o annotazioni. class Persona constructor(nome: String) { } // normalmente si scrive così class Persona(nome: String) { } Classi
assegniamo a campi omonimi. In Kotlin posso farlo facilemtne: class Persona( val nome: String, val età: Int, val madre: Persona, val padre: Persona) { … } Classi
$nome") // stampa su schermo "Ciao Michele” // in Java sarebbe "ciao" + nome • String interpolation con espressioni arbitrarie println("Ciao ${nome.toUpperCase()}") // stampa su schermo "Ciao MICHELE" Stringhe
String.siiCortese() = "${this}, per piacere” var richiesta = "Passa il sale" println(testo.siiCortese()) // stampa Passa il sale, per piacere Questi metodi però sono solo syntactic sugar: non modificano la classe originale e non possono accedere ai membri privati della classe Estendere le classi esistenti
Int) • Ottieni getters e setters (solo per variabili) per leggere e scrivere tutte le proprietà e costruttore • equals(), hashCode() e copy() per gestire oggetti, ovvero confrontarli e copiarli Data classes
Int) • Ottieni getters e setters (solo per variabili) per leggere e scrivere tutte le proprietà e costruttore • equals(), hashCode() e copy() per gestire oggetti, ovvero confrontarli e copiarli • toString() per la stampa di un oggetto in forma leggibile Name_of_the_class(Name_of_the_variable=Value_of_t he_variable, /* .. */)” Data classes
Int) • Ottieni getters e setters (solo per variabili) per leggere e scrivere tutte le proprietà e costruttore • equals(), hashCode() e copy() per gestire oggetti, ovvero confrontarli e copiarli • toString() per la stampa di un oggetto in forma leggibile Name_of_the_class(Name_of_the_variable=Value_of_t he_variable, /* .. */)” • component1() .. componentN() per tutte le proprietà in ordine di dichiarazione. Usate per destructuring declaration Data classes
data classes) non può contenere alcun codice. Per ovviare a questo problema si possono usare i blocchi di inizializzazione, che sono identificati dalla keyword init. Nei blocchi si può accedere alle proprietà definite nel costruttore primario. Data classes
Int) { init { // accesso alle proprietà definite // nel costruttore val account = "${name.toLowerCase()} + ${age}” recordNewAccount(account) } } Data classes
was designing the first comprehensive type system for references in an object-oriented language. My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. – Tony Hoare, inventor of ALGOL W.
val numero: Int = 1 val numero1: Int = null // invalido, non compila var stringa: String = "testo" var stringa1: String = null // invalido, non compila Nullability
essere nullo non lo sia nel caso attuale. Per quei casi c’è l’operatore !!. Se sbagliamo però… NPE data class Identifier(val person : Boolean, val codiceFiscale: String?, val partitaIva: String?) { init { require(!person || (codiceFiscale != null && partitaIva == null)) require(person || (codiceFiscale == null && partitaIva != null)) } … } Nullability
essere nullo non lo sia nel caso attuale. Per quei casi c’è l’operatore !!. Se sbagliamo però… NPE class Identifier(…) { … fun code() : String? { if (person) { return codiceFiscale } return partitaIva } … } Nullability
essere nullo non lo sia nel caso attuale. Per quei casi c’è l’operatore !!. Se sbagliamo però… NPE class Identifier(…) { … fun code() : String { if (person) { return codiceFiscale!! } return partitaIva!! } … } Nullability
nullo. Per quei casi abbiamo l’operatore Elvis ?: fun cleanList(list: List<Int>?) : List<Int> { return list ?: emptyList<Int>() } fun sizeOfList(list: List<Int>?) : Int { return list?.size ?: 0 } Nullability
certe situazioni il compilatore è in grado di applicare un’operazione di cast automaticamente. fun printSize(value: Any) { if (value is String) { println("String of size ${value.length}") } if (value is List<*>) { println("List of size ${value.size}") } } Smart cast & Safe cast
e while. Lo smart cast è applicato anche quando è presente un !is seguito da un return o con un is dalle operazione logiche and (&&) e or (||). Lo smart cast non è applicato quando il compilatore non è in grado di garantire che la variabile non sia cambiata tra il controllo e l’uso. Smart cast & Safe cast
// testo è automaticamente convertito in una String // a destra di && if (a is Int && a > 0) { println(a) // a è automaticamente convertito in Int } Smart cast & Safe cast
sicura. Ciò significa che se il cast fallisce viene lanciata un’eccezione. Ad esempio se il valore è nullo, ma il tipo in cui deve essere trasformato non lo accetta viene lanciata un’eccezione. Per questo viene definito unsafe cast. Un modo per evitare questo rischio è usare l’operatore di cast con tipi annullabili. // null è valido per b as String? val a: String? = b as String? Smart cast & Safe cast
safe cast (as?), se il cast fallisce ritorna null. Comunque il valore che riceve il risultato dell’operazione di cast deve essere in grado di accettare il null. // la funzione ritorna String? (non String) fun stringOrNull(value: Any) : String? { // as? String è valido anche se value è null return value as? String } println(stringOrNull("string")) println(stringOrNull(1)) Smart cast & Safe cast