Kotlin è un linguaggio di programmazione moderno che viene compilato in bytecode Java. È gratuito e open source e promette di rendere la codifica per Android ancora più divertente.
Nell'articolo precedente, hai imparato a conoscere classi e oggetti in Kotlin. In questo tutorial, continueremo a saperne di più sulle proprietà e analizzeremo anche i tipi avanzati di classi in Kotlin esplorando quanto segue:
Possiamo dichiarare una proprietà non nullo in Kotlin come tardo-inizializzato. Ciò significa che una proprietà non nullo non verrà inizializzata al momento della dichiarazione con un valore - l'effettiva inizializzazione non avverrà tramite alcun costruttore - ma, invece, verrà inizializzata in ritardo da un metodo o un'iniezione di dipendenza.
Diamo un'occhiata a un esempio per capire questo modificatore di proprietà unico.
class Presenter repository private var: repository? = null fun initRepository (repository: repository): Unit this.repository = repo class Repository fun saveAmount (amount: Double)
Nel codice sopra, abbiamo dichiarato nullable mutabile deposito
proprietà che è di tipo deposito
-all'interno della classe Presentatore
-e abbiamo quindi inizializzato questa proprietà su null durante la dichiarazione. Abbiamo un metodo initRepository ()
nel Presentatore
classe che reinizializza questa proprietà in seguito con un effettivo deposito
esempio. Si noti che a questa proprietà può essere assegnato anche un valore utilizzando un injector di dipendenza come Dagger.
Ora per noi invocare metodi o proprietà su questo deposito
proprietà, dobbiamo fare un controllo nullo o utilizzare l'operatore di chiamata sicura. Perché? Perché il deposito
la proprietà è di tipo nullable (deposito?
). (Se hai bisogno di un aggiornamento sul nullability in Kotlin, visita gentilmente Nullability, Loops e Conditions).
// Inside Presenter fun salva divertenti (importo: Double) repository? .SaveAmount (amount)
Per evitare di dover eseguire controlli nulli ogni volta che è necessario richiamare il metodo di una proprietà, possiamo contrassegnare tale proprietà con lateinit
modificatore-questo significa che abbiamo dichiarato tale proprietà (che è un'istanza di un'altra classe) come tardo-inizializzato (significa che la proprietà verrà inizializzata in seguito).
class Presenter private lateinit var repository: Repository // ...
Ora, finché aspetteremo che alla proprietà sia stato assegnato un valore, possiamo accedere tranquillamente ai metodi della proprietà senza effettuare controlli nulli. L'inizializzazione della proprietà può avvenire in un metodo setter o tramite dipendenza.
repository.saveAmount (quantità)
Notare che se proviamo ad accedere ai metodi della proprietà prima che sia stato inizializzato, otterremo un kotlin.UninitializedPropertyAccessException
invece di un NullPointerException
. In questo caso, il messaggio di eccezione sarà "il repository di proprietà lateinit non è stato inizializzato".
Notare anche le seguenti restrizioni poste quando si ritardano l'inizializzazione di una proprietà con lateinit
:
var
).Int
, Doppio
, Galleggiante
, e così via. In Advanced Functions, ho introdotto il in linea
modificatore per funzioni di ordine superiore: questo aiuta a ottimizzare le funzioni di ordine superiore che accettano un lambda come parametro.
In Kotlin, possiamo anche usare questo in linea
modificatore sulle proprietà. L'utilizzo di questo modificatore ottimizzerà l'accesso alla proprietà.
Vediamo un esempio pratico.
class Student val nickName: String get () println ("Nick name recuperato") restituisce "koloCoder" fun main (args: Array) val student = Student () print (student.nickName)
Nel codice sopra, abbiamo una proprietà normale, nickName
, quello non ha il in linea
modificatore. Se decompiliamo lo snippet di codice, usando il Mostra Kotlin Bytecode funzione (se sei in IntelliJ IDEA o Android Studio, usa Utensili > Kotlin > Mostra Kotlin Bytecode), vedremo il seguente codice Java:
public final class Student @NotNull public final String getNickName () String var1 = "Nick name recuperato"; System.out.println (Q1); ritorna "koloCoder"; public final class InlineFunctionKt public static final void main (@NotNull String [] args) Intrinsics.checkParameterIsNotNull (args, "args"); Studente = nuovo studente (); String var2 = student.getNickName (); System.out.print (var2);
Nel codice Java generato sopra (alcuni elementi del codice generato sono stati rimossi per brevità), puoi vedere che all'interno di principale()
metodo che il compilatore ha creato a Alunno
oggetto, chiamato il getNickName ()
metodo, quindi stampato il suo valore di ritorno.
Specifichiamo ora la proprietà come in linea
invece, e confronta il codice byte generato.
// ... inline val nickName: String // ...
Inseriamo semplicemente il in linea
modificatore prima del modificatore di variabile: var
o val
. Ecco il bytecode generato per questa proprietà inline:
// ... public static final void main (@NotNull String [] args) Intrinsics.checkParameterIsNotNull (args, "args"); Studente = nuovo studente (); String var3 = "Nick name recuperato"; System.out.println (VAR3); String var2 = "koloCoder"; System.out.print (var2); // ...
Di nuovo un codice è stato rimosso, ma la cosa fondamentale da notare è la principale()
metodo. Il compilatore ha copiato la proprietà ottenere()
corpo della funzione e incollato nel sito di chiamata (questo meccanismo è simile alle funzioni inline).
Il nostro codice è stato ottimizzato perché non è necessario creare un oggetto e chiamare il metodo getter della proprietà. Ma, come discusso nelle funzioni inline post, avremmo un codice byte più grande di prima, quindi usatelo con cautela.
Si noti inoltre che questo meccanismo funzionerà per le proprietà che non dispongono di un campo di supporto (ricordare, un campo di supporto è solo un campo che viene utilizzato dalle proprietà quando si desidera modificare o utilizzare tali dati di campo).
Nelle funzioni avanzate ho anche discusso delle funzioni di estensione, queste ci danno la possibilità di estendere una classe con nuove funzionalità senza dover ereditare da quella classe. Kotlin fornisce anche un meccanismo simile per le proprietà, chiamato proprietà di estensione.
val String.upperCaseFirstLetter: String get () = this.substring (0, 1) .toUpperCase (). plus (this.substring (1))
Nel post delle funzioni avanzate abbiamo definito a uppercaseFirstLetter ()
funzione di estensione con tipo di ricevitore Stringa
. Qui, invece, l'abbiamo convertito in una proprietà di estensione di livello superiore. Nota che devi definire un metodo getter sulla tua proprietà affinché funzioni.
Quindi, con questa nuova conoscenza delle proprietà dell'estensione, saprai che se hai mai desiderato che una classe avesse una proprietà che non era disponibile, sei libero di creare una proprietà di estensione di quella classe.
Iniziamo con una tipica classe Java o POJO (Plain Old Java Object).
public class BlogPost private final String title; url URI finale privato; descrizione String privata finale; privato finale Data publishDate; // ... costruttore non incluso per brevità @Override public boolean equals (Object o) if (this == o) return true; if (o == null || getClass ()! = o.getClass ()) restituisce false; BlogPost blogPost = (BlogPost) o; if (title! = null?! title.equals (blogPost.title): blogPost.title! = null) return false; if (url! = null?! url.equals (blogPost.url): blogPost.url! = null) return false; if (description! = null?! description.equals (blogPost.description): blogPost.description! = null) return false; return publishDate! = null? publishDate.equals (blogPost.publishDate): blogPost.publishDate == null; @Override public int hashCode () int result = title! = Null? title.hashCode (): 0; risultato = 31 * risultato + (url! = null? url.hashCode (): 0); risultato = 31 * risultato + (descrizione! = null? description.hashCode (): 0); risultato = 31 * risultato + (publishDate! = null? publishDate.hashCode (): 0); ritorno risultato; @Override public String toString () return "BlogPost " + "title = '" + title +' \ "+", url = "+ url +", + description + "\" + ", publishDate =" + publishDate + ''; // ... setter e getter anche ignorati per brevità
Come puoi vedere, dobbiamo codificare esplicitamente gli accessor di proprietà della classe: getter e setter, nonché codice hash
, è uguale a
, e accordare
metodi (sebbene IntelliJ IDEA, Android Studio o la libreria AutoValue possano aiutarci a generarli). Vediamo questo tipo di codice boilerplate principalmente nel livello dati di un tipico progetto Java. (Ho rimosso gli accessori di campo e il costruttore per brevità).
La cosa bella è che il team di Kotlin ci ha fornito il dati
modificatore per le classi per eliminare la scrittura di questi caratteri.
Scriviamo ora il codice precedente in Kotlin.
classe dati BlogPost (var title: String, var url: URI, var descrizione: String, var publishDate: Date)
Eccezionale! Specifichiamo solo il dati
modificatore prima del classe
parola chiave per creare una classe di dati, proprio come quello che abbiamo fatto nella nostra Post sul blog
Classe Kotlin sopra. Ora il è uguale a
, codice hash
, accordare
, copia
, e per noi saranno creati sotto molteplici aspetti metodi multipli. Si noti che una classe di dati può estendere altre classi (questa è una nuova funzionalità di Kotlin 1.1).
è uguale a
MetodoQuesto metodo confronta due oggetti per l'uguaglianza e restituisce true se sono uguali o falsi altrimenti. In altre parole, confronta se le due istanze di classe contengono gli stessi dati.
student.equals (student3) // utilizzando il == nello studente Kotlin == student3 // uguale all'utilizzo di equals ()
In Kotlin, usando l'operatore di uguaglianza ==
chiamerà il è uguale a
metodo dietro le quinte.
codice hash
Metodo Questo metodo restituisce un valore intero utilizzato per l'archiviazione veloce e il recupero dei dati memorizzati in una struttura di raccolta dati basata su hash, ad esempio nel HashMap
e HashSet
tipi di raccolta.
accordare
MetodoQuesto metodo restituisce a Stringa
rappresentazione di un oggetto.
data class Person (var firstName: String, var lastName: String) val person = Person ("Chike", "Mgbemena") println (person) // stampa "Person (firstName = Chike, lastName = Mgbemena)"
Chiamando semplicemente l'istanza della classe, otteniamo un oggetto stringa restituito a noi-Kotlin chiama l'oggetto accordare()
sotto il cofano per noi. Ma se non mettiamo il dati
parola chiave in, vedere quale sarebbe la nostra rappresentazione stringa di oggetti:
com.chike.kotlin.classes.Person@2f0e140b
Molto meno informativo!
copia
MetodoQuesto metodo ci consente di creare una nuova istanza di un oggetto con tutti gli stessi valori di proprietà. In altre parole, crea una copia dell'oggetto.
val person1 = Person ("Chike", "Mgbemena") println (person1) // Person (firstName = Chike, lastName = Mgbemena) val person2 = person1.copy () println (person2) // Person (firstName = Chike, lastName = Mgbemena)
Una cosa interessante del copia
il metodo in Kotlin è la possibilità di modificare le proprietà durante la copia.
val person3 = person1.copy (lastName = "Onu") println (person3) // Person3 (firstName = Chike, lastName = Onu)
Se sei un programmatore Java, questo metodo è simile al clone()
metodo con cui hai già familiarità. Ma il Kotlin copia
il metodo ha caratteristiche più potenti.
Nel Persona
classe, abbiamo anche due metodi generati automaticamente dal compilatore a causa del dati
parola chiave inserita nella classe. Questi due metodi sono preceduti da "componente", seguito da un suffisso numerico: Component1 ()
, Component2 ()
. Ciascuno di questi metodi rappresenta le singole proprietà del tipo. Si noti che il suffisso corrisponde all'ordine delle proprietà dichiarate nel costruttore principale.
Quindi, nel nostro esempio di chiamata Component1 ()
restituirà il nome e la chiamata Component2 ()
restituirà il cognome.
println (person3.component1 ()) // Chike println (person3.component2 ()) // Onu
Chiamare le proprietà usando questo stile è difficile da capire e leggere, quindi chiamare il nome della proprietà in modo esplicito è molto meglio. Tuttavia, queste proprietà create in modo implicito hanno uno scopo molto utile: ci permettono di fare una dichiarazione di destrutturazione, in cui possiamo assegnare ciascun componente a una variabile locale.
val (firstName, lastName) = Person ("Angelina", "Jolie") println (firstName + "" + lastName) // Angelina Jolie
Quello che abbiamo fatto qui è assegnare direttamente la prima e la seconda proprietà (nome di battesimo
e cognome
) del Persona
digita alle variabili nome di battesimo
e cognome
rispettivamente. Ho anche discusso di questo meccanismo noto come dichiarazione di destrutturazione nell'ultima sezione del post Pacchetti e funzioni di base.
Nel post More Fun With Functions, ti ho detto che Kotlin supporta funzioni locali o nidificate, una funzione dichiarata all'interno di un'altra funzione. Bene, anche Kotlin supporta le classi annidate, una classe creata all'interno di un'altra classe.
class OuterClass class NestedClass fun nestedClassFunc ()
Chiamiamo anche le funzioni pubbliche della classe annidata come visto di seguito: una classe nidificata in Kotlin è equivalente a a statico
classe nidificata in Java. Si noti che le classi nidificate non possono memorizzare un riferimento alla loro classe esterna.
val nestedClass = OuterClass.NestedClass () nestedClass.nestedClassFunc ()
Siamo anche liberi di impostare la classe nidificata come privata, questo significa che possiamo solo creare un'istanza di NestedClass
nell'ambito del OuterClass
.
Le classi interne, d'altra parte, possono fare riferimento alla classe esterna in cui è stato dichiarato. Per creare una classe interiore, poniamo il interno
parola chiave prima del classe
parola chiave in una classe annidata.
class OuterClass () val oCPropt: String = "Yo" inner class InnerClass fun innerClassFunc () val outerClass = this @ OuterClass print (outerClass.oCPropt) // stampa "Yo"
Qui facciamo riferimento al OuterClass
dal classe interna
usando questo @ OuterClass
.
Un tipo enum dichiara un insieme di costanti rappresentate da identificatori. Questo tipo speciale di classe è creato dalla parola chiave enum
che è specificato prima del classe
parola chiave.
enum class Paese NIGERIA, GHANA, CANADA
Per recuperare un valore enum basato sul suo nome (proprio come in Java), facciamo questo:
Country.valueOf ( "NIGERIA")
Oppure possiamo usare il Kotlin enumValueOf
metodo di supporto per accedere alle costanti in modo generico:
enumValueOf("NIGERIA")
Inoltre, possiamo ottenere tutti i valori (come per un enum Java) come questo:
Country.values ()
Finalmente, possiamo usare il Kotlin EnumValues
metodo di supporto per ottenere tutte le voci enum in modo generico:
EnumValues()
Questo restituisce una matrice contenente le voci enum.
Proprio come una classe normale, il enum
type può avere il proprio costruttore con proprietà associate a ciascuna costante enum.
enum class Paese (val callingCode: Int) NIGERIA (234), USA (1), GHANA (233)
Nel Nazione
Enum tipo costruttore primario, abbiamo definito la proprietà immutabile callingCodes
per ogni costante enum. In ciascuna delle costanti, abbiamo passato un argomento al costruttore.
Possiamo quindi accedere alla proprietà constants in questo modo:
val country = Country.NIGERIA print (country.callingCode) // 234
Una classe sigillata in Kotlin è una classe astratta (non hai mai intenzione di creare oggetti da essa) che altre classi possono estendere. Queste sottoclassi sono definite all'interno del corpo della classe sigillata, nello stesso file. Poiché tutte queste sottoclassi sono definite all'interno del corpo della classe sigillata, possiamo conoscere tutte le sottoclassi possibili semplicemente visualizzando il file.
Vediamo un esempio pratico.
// shape.kt sealed class Forma class Circle: Shape () class Triangolo: Shape () class Rectangle: Shape ()
Per dichiarare una classe come sigillata, inseriamo il sigillato
modificatore prima del classe
modificatore nell'intestazione della dichiarazione di classe: nel nostro caso, abbiamo dichiarato il Forma
classe come sigillato
. Una classe sigillata è incompleta senza le sue sottoclassi - proprio come una tipica classe astratta - quindi dobbiamo dichiarare le singole sottoclassi all'interno dello stesso file (shape.kt in questo caso). Si noti che non è possibile definire una sottoclasse di una classe sigillata da un altro file.
Nel nostro codice sopra, abbiamo specificato che il Forma
la classe può essere estesa solo dalle classi Cerchio
, Triangolo
, e Rettangolo
.
Le classi sigillate in Kotlin hanno le seguenti regole aggiuntive:
astratto
in una classe sigillata, ma questo è ridondante perché le classi sigillate sono astratte per impostazione predefinita.Aperto
o finale
modificatore. Le classi che estendono sottoclassi di una classe sigillata possono essere inserite nello stesso file o in un altro file. La sottoclasse della classe sigillata deve essere contrassegnata con Aperto
modificatore (imparerai di più sull'ereditarietà di Kotlin nel prossimo post).
// employee.kt sealed class Employee open class Artista: Employee () // musician.kt class Musicista: Artist ()
Una classe sigillata e le sue sottoclassi sono davvero utili in a quando
espressione. Per esempio:
fun whatIsIt (shape: Shape) = when (shape) is Circle -> println ("A circle") è Triangle -> println ("A triangle") è Rectangle -> println ("A rectangle")
Qui il compilatore è intelligente per garantire che abbiamo coperto tutto il possibile quando
casi. Ciò significa che non è necessario aggiungere il altro
clausola.
Se dovessimo fare quanto segue invece:
fun whatIsIt (shape: Shape) = when (shape) is Circle -> println ("A circle") è Triangle -> println ("A triangle")
Il codice non verrà compilato, perché non abbiamo incluso tutti i casi possibili. Avremmo il seguente errore:
Kotlin: "quando" l'espressione deve essere esaustiva, aggiungi invece il ramo "è rettangolare" o "altro".
Quindi potremmo includere il è Rectangle
caso o includere il altro
clausola per completare il quando
espressione.
In questo tutorial, hai imparato di più sulle classi in Kotlin. Abbiamo trattato quanto segue sulle proprietà della classe:
Inoltre, hai imparato alcune classi interessanti e avanzate come dati, enum, classi nidificate e sigillate. Nel prossimo tutorial della serie Kotlin From Scratch, verrai presentato alle interfacce e all'ereditarietà di Kotlin. A presto!
Per saperne di più sulla lingua di Kotlin, ti consiglio di visitare la documentazione di Kotlin. Oppure guarda alcuni dei nostri altri post di sviluppo di app per Android qui su Envato Tuts!