Kotlin de la zero Proprietăți avansate și clase

Kotlin este un limbaj de programare modern care se compilează la Java bytecode. Este gratuit și open source, și promite să facă codificarea pentru Android chiar mai distractiv.  

În articolul precedent, ați aflat despre clase și obiecte din Kotlin. În acest tutorial, vom continua să aflăm mai multe despre proprietăți și, de asemenea, să examinăm tipurile de cursuri avansate de la Kotlin, explorând următoarele:

  • proprietăți întârziate-inițializate
  • inline properties 
  • proprietăți de extindere
  • date, enum, imbricate și clase sigilate

1. Proprietăți întârziate-inițializate

Putem declara o proprietate non-null in Kotlin as târziu-inițializat. Aceasta înseamnă că o proprietate non-null nu va fi inițializată la momentul declarației, cu o inițializare valori-reale nu se va întâmpla prin nici un constructor - dar în schimb va fi întârziată inițializată printr-o metodă sau o injecție de dependență.

Să examinăm un exemplu pentru a înțelege acest modificator de proprietate unic. 

clasa Presenter depozit privat var: Depozit? = null fun initRepository (repo: Repository): Unitatea this.repository = repo clasa Repository fun saveAmount (suma: Double)  

În codul de mai sus, am declarat că nu poate fi mutat repertoriu proprietate care este de tip Repertoriu-în interiorul clasei Prezentator-și am inițializat această proprietate la nulă în timpul declarației. Avem o metodă initRepository () în Prezentator clasă care reinicializează mai târziu această proprietate cu o realitate Repertoriu instanță. Rețineți că acestei proprietăți i se poate atribui o valoare utilizând un injector de dependență, cum ar fi Dagger.     

Acum, pentru noi, să invocăm metode sau proprietăți în acest sens repertoriu proprietate, trebuie să facem o verificare nulă sau să folosim operatorul de apel în condiții de siguranță. De ce? Deoarece repertoriu proprietatea este de tip nullabil (Repertoriu?). (Dacă aveți nevoie de o reîmprospătare a nulității în Kotlin, vă rugăm să vizitați Nullabilitate, Loops și condiții).

// Clasa Inside Presenter distracție salvați (suma: Dublă) repository? .SaveAmount (sumă)

Pentru a evita efectuarea de verificări null de fiecare dată când trebuie să invocăm o metodă a unei proprietăți, putem marca acea proprietate cu lateinit modificator - aceasta înseamnă că am declarat că proprietatea (care este o instanță a unei alte clase) este târziu-inițializat (adică proprietatea va fi inițializată mai târziu).  

clasă Presenter private lateinit var repository: Repository // ...

Acum, atâta timp cât așteptăm până când proprietatea a dat o valoare, suntem siguri să accesăm metodele proprietății fără a efectua nicio verificare nulă. Initializarea proprietății se poate realiza fie printr-o metodă de setare, fie prin injecție de dependență. 

repository.saveAmount (suma)

Rețineți că dacă încercăm să accesăm metode ale proprietății înainte de a fi inițializată, vom obține o kotlin.UninitializedPropertyAccessException în loc de un NullPointerException. În acest caz, mesajul de excepție va fi "repositoarea de proprietăți de la sfârșitul anului nu a fost inițializată". 

Rețineți și următoarele restricții la întârzierea inițializării unei proprietăți cu lateinit:

  • Trebuie să fie mutable (declarată cu var).
  • Tipul de proprietate nu poate fi un tip primitiv, de exemplu, Int, Dubla, Pluti, si asa mai departe. 
  • Proprietatea nu poate avea un picker personalizat sau setter.

2. Proprietăți inline

În funcțiile avansate, am introdus in linie modificator pentru funcții de ordin superior - aceasta ajută la optimizarea oricăror funcții de ordin superior care acceptă o valoare lambda ca parametru. 

În Kotlin, putem folosi și acest lucru in linie modificator asupra proprietăților. Utilizarea acestui modificator va optimiza accesul la proprietate.

Să vedem un exemplu practic. 

Class Student_value: String get () println ("Nick name retrieved") retur "koloCoder" fun main (args: Array) student val = Student () print (student.nickName)

În codul de mai sus, avem o proprietate normală, poreclă, care nu are in linie modificator. Dacă decomprimăm fragmentul de cod, folosiți Afișați Bytecode Kotlin (dacă vă aflați în IntelliJ IDEA sau Android Studio, utilizați Unelte > Kotlin > Afișați Bytecode Kotlin), vom vedea următorul cod Java:

clasa publica finala a studentilor @NotNull public final String getNickName () String var1 = "Numele nick-ului primit"; System.out.println (var1); retur "wheelCoder";  public clasa finală InlineFunctionKt public static final void principal (@NotNull String [] args) Intrinsics.checkParameterIsNotNull (args, "args"); Student student = student nou (); Stringul var2 = student.getNickName (); System.out.print (var2); 

În codul Java generat de mai sus (unele elemente ale codului generat au fost eliminate pentru dragul brevetului), puteți vedea că în interiorul principal() metoda pe care compilatorul a creat - o Student obiect, numit getNickName () , apoi tipăriți valoarea returnată.  

Să specificăm acum proprietatea ca in linie în schimb, și comparați octetul generat.

// ... inline val nickName: String // ... 

Introducem doar in linie modificator înainte de modificatorul variabil: var sau Val. Iată octetul generat pentru această proprietate inline:

// ... static public final void principal (@NotNull String [] args) Intrinsics.checkParameterIsNotNull (args, "args"); Student student = student nou (); String var3 = "Numele de prenume recuperat"; System.out.println (var3); String var2 = "colocator"; System.out.print (var2);  // ... 

Din nou, un anumit cod a fost eliminat, dar cheia care trebuie luată în considerare este principal() metodă. Compilatorul a copiat proprietatea obține() funcția organism și lipit-o în site-ul de apel (acest mecanism este similar cu funcțiile inline). 

Codul nostru a fost optimizat din cauza lipsei de a crea un obiect și de a apela metoda getter-ului. Dar, după cum sa discutat în postul de funcții inline, am avea un ocol mai mare decât înainte - deci folosiți cu prudență. 

Rețineți, de asemenea, că acest mecanism va funcționa pentru proprietăți care nu au un câmp de asistență (rețineți că un câmp de asistență este doar un câmp care este utilizat de proprietăți atunci când doriți să modificați sau să utilizați acele date din câmp). 

3. Proprietăți extensii 

În funcțiile avansate am discutat, de asemenea, despre funcții de extensie - acestea ne dau posibilitatea de a extinde o clasă cu noi funcționalități fără a trebui să moștenim din acea clasă. Kotlin oferă, de asemenea, un mecanism similar pentru proprietăți, numit proprietăți de extindere

val String.upperCaseFirstLetter: String get () = this.substring (0, 1) .toUpperCase () plus (this.substring (1))

În postul Advanced Functions am definit a uppercaseFirstLetter () funcția de extensie cu tipul receptorului Şir. Aici am transformat-o într-o proprietate de extensie la cel mai înalt nivel. Rețineți că trebuie să definiți o metodă getter pe proprietatea dvs. pentru ca aceasta să funcționeze. 

Prin urmare, cu aceste noi cunoștințe despre proprietățile de extensie, veți ști că, dacă doriți vreodată ca o clasă să aibă o proprietate care nu a fost disponibilă, aveți libertatea de a crea o proprietate de extindere a acelei clase. 

4. Clase de date

Să începem cu o clasă Java tipică sau POJO (obiect obișnuit Java obișnuit). 

clasă publică BlogPost final final String; adresa URL privată finală URI; descriere finală privată a șirului; finală privată Data publicăriiDate; // constructorul nu este inclus pentru brevetitate @Override public boolean equals (Object o) if (this == o) return true; dacă (o == null || getClass ()! = o.getClass ()) return false; BlogPost blogPost = (BlogPost) o; dacă (titlu! = null?! title.equals (blogPost.title): blogPost.title! = null) return false; dacă (url! = null?! url.equals (blogPost.url): blogPost.url! = null) return false; dacă descrierea! = null?! description.equals (blogPost.description): blogPost.description! = null) return false; retur publicDate! = null? publishDate.equals (blogPost.publishDate): blogPost.publishDate == null;  @Overide public int hashCode () int rezultat = titlu! = Null? title.hashCode (): 0; rezultat = 31 * rezultat + (url! = null? url.hashCode (): 0); rezultat = 31 * rezultat + (descrierea! = null? description.hashCode (): 0); rezultatul = 31 * rezultat + (publishDate! = null? publishDate.hashCode (): 0); rezultatul retur;  @Override public String toString () return "BlogPost " + "title = '+ + titlu +', + url + publishDate + '';  // ... setteri și getters, de asemenea, ignorați pentru dragul brevity

După cum puteți vedea, trebuie să codificăm în mod explicit accesoriile proprietății clasei: getter și setter, precum și hashCodeeste egală, și toString (deși IntelliJ IDEA, Android Studio sau biblioteca AutoValue ne poate ajuta să le generăm). Vom vedea acest tip de cod de boilerplate în cea mai mare parte în stratul de date al unui proiect tipic Java. (Am scos accesoriile de teren și constructorul pentru dragul lor). 

Lucrul interesant este că echipa Kotlin ne-a oferit date modifier pentru clasele de a elimina scrierea acestor boilerplate.

Să scriem acum codul precedent în Kotlin.

clasa de date BlogPost (titlul de var: String, var url: URI, var descriere: String, var publishDate: Data)

Minunat! Specificăm doar date modificator înainte de clasă cuvânt cheie pentru a crea o clasă de date - la fel ca și în ceea ce le-am făcut Postare pe blog Clasa Kotlin de mai sus. Acum este egalăhashCodetoStringcopie, și mai multe metode componente vor fi create sub capota pentru noi. Rețineți că o clasă de date poate extinde alte clase (aceasta este o caracteristică nouă a lui Kotlin 1.1). 

 este egală Metodă

Această metodă compară două obiecte pentru egalitate și returnează adevărat dacă sunt egale sau false altfel. Cu alte cuvinte, compară dacă cele două instanțe de clasă conțin aceleași date. 

student.equals (student3) // folosind == în studentul Kotlin == student3 // același lucru ca folosind equals ()

În Kotlin, folosind operatorul de egalitate == va apela este egală metoda din spatele scenei.

 hashCode Metodă 

Această metodă returnează o valoare intregă utilizată pentru stocarea rapidă și recuperarea datelor stocate într-o structură de date bazată pe hash, de exemplu în HashMap și HashSet tipuri de colecții.  

 toString Metodă

Această metodă returnează a Şir reprezentarea unui obiect. 

date personale (varName: String, var lastName: String) val person = Persoană ("Chike", "Mgbemena") println

Doar prin apelarea instanței de clasă, primim un obiect șir returnat la noi - Kotlin sună obiectul toString () sub capota pentru noi. Dar dacă nu punem date cuvânt cheie în, a vedea ceea ce reprezentarea șir de obiect ar fi: 

com.chike.kotlin.classes.Person@2f0e140b

Mult mai puțin informativ!

 copie Metodă

Această metodă ne permite să creăm o nouă instanță a unui obiect cu toate aceleași valori de proprietate. Cu alte cuvinte, creează o copie a obiectului. 

val person1 = Persoană ("Chike", "Mgbemena") println (person1) // Persoană (FirstName = Chike, lastName = Mgbemena) = Mgbemena)

Un lucru minunat despre copie Metoda în Kotlin este abilitatea de a schimba proprietățile în timpul copierii. 

val person3 = person1.copy (lastName = "Onu") println (person3) // Person3 (firstName = Chike, lastName = Onu)

Dacă sunteți coder Java, această metodă este similară cu cea de clona () cu care sunteți deja familiarizați. Dar Kotlin copie metoda are caracteristici mai puternice. 

Declarație distructivă

În Persoană clasa, avem, de asemenea, două metode generate automat de noi de către compilator din cauza date cuvântul cheie plasat în clasă. Aceste două metode sunt prefixate cu "component", urmate de un sufix de număr: component1 ()component2 (). Fiecare dintre aceste metode reprezintă proprietățile individuale ale tipului. Rețineți că sufixul corespunde ordinii proprietăților declarate în constructorul principal.

Deci, în apelul nostru de exemplu component1 () va întoarce primul nume și va apela component2 () va returna numele de familie.

println (person3.component1 ()) // Chike println (person3.component2 ()) // Onu

Apelarea proprietăților utilizând acest stil este dificil de înțeles și de citit, însă apelarea explicit a numelui proprietății este mult mai bună. Cu toate acestea, aceste proprietăți create implicit au un scop foarte util: ne permit să facem o declarație de distrugere, în care putem atribui fiecare componentă unei variabile locale.

val (nume, prenume) = persoana ("Angelina", "Jolie") println (firstName + "" + lastName)

Ce am făcut aici este atribuirea directă a primelor și a doua proprietăți (Nume și numele de familie) din Persoană tip la variabile Nume și numele de familie respectiv. Am discutat, de asemenea, acest mecanism cunoscut sub numele de declarație de distrugere în ultima secțiune a mesajului "Pachete și funcții de bază". 

5. Clasele încorporate

În postul Mai mult fun cu funcții, v-am spus că Kotlin are suport pentru funcții locale sau imbricate - o funcție declarată în interiorul unei alte funcții. Ei bine, Kotlin susține, de asemenea, în mod similar, clase imbricate - o clasă creată într-o altă clasă. 

clasa OuterClass class NestedClass distracție nestedClassFunc () 

Apelăm chiar funcțiile publice ale clasei imbricate așa cum se vede mai jos - o clasă imbricată în Kotlin este echivalentă cu a static clasa imbricata in Java. Rețineți că clasele imbricate nu pot stoca o referință la clasa lor exterioară. 

val nestedClass = OuterClass.NestedClass () nestedClass.nestedClassFunc ()

De asemenea, suntem liberi să setăm clasa imbricată ca privată - aceasta înseamnă că putem crea doar o instanță a NestedClass în cadrul domeniului OuterClass

Clasa interioară

Clasele interioare, pe de altă parte, pot face referire la clasa exterioară în care a fost declarată. Pentru a crea o clasă interioară, plasăm interior cuvânt cheie înainte de clasă cuvânt cheie într-o clasă imbricată. 

clasa OuterClass () val oCPropt: String = "Yo" clasa interioara InnerClass fun interiorClassFunc () val exteriorClass = aceasta @ OuterClass print (outerClass.oCPropt) // imprimă "Yo"

Aici facem referire la OuterClass de la InnerClass prin utilizarea  acest @ OuterClass.

6. Clasele Enum

Un tip enum declară un set de constante reprezentate de identificatori. Acest tip special de clasă este creat de cuvântul cheie enum care este specificat înainte de clasă cuvinte cheie. 

clasa enum Țara NIGERIA, GHANA, CANADA

Pentru a prelua o valoare enum pe baza numelui ei (la fel ca în Java), facem acest lucru:

Country.valueOf ( "NIGERIA")

Sau putem folosi Kotlin enumValueOf() metoda helper pentru a accesa constantele într-un mod generic:

enumValueOf("NIGERIA")

De asemenea, putem obține toate valorile (ca pentru un enum Java) astfel:

Country.values ​​()

În cele din urmă, putem folosi Kotlin EnumValues() metoda helper pentru a obține toate intrările enum într-un mod generic:

EnumValues()

Aceasta returnează o matrice care conține intrările enum.  

Enum Constructori

La fel ca o clasă normală enum tip poate avea propriul constructor cu proprietăți asociate fiecărei constante enum. 

clasa enum Țara (val callingCode: Int) NIGERIA (234), SUA (1), GHANA (233)

În Țară enum, am definit proprietatea imuabilă callingCodes pentru fiecare constantă enum. În fiecare dintre constante, am dat un argument constructorului. 

Putem apoi să accesăm proprietățile constante, astfel:

val țară = țara.NIGERIA print (country.callingCode) // 234

7. Clasele închise

O clasă sigilată în Kotlin este o clasă abstractă (nu intenționați niciodată să creați obiecte din ea) pe care alte clase le pot extinde. Aceste subclase sunt definite în interiorul corpului clasei închise - în același fișier. Deoarece toate aceste subclase sunt definite în interiorul corpului de clasă închis, putem cunoaște toate subclasele posibile prin vizualizarea pur și simplu a fișierului. 

Să vedem un exemplu practic. 

// shape.kt clasă închisă Formă de formă Cerc: Formă de formă () Triunghi: Formă () clasă Dreptunghi: Formă ()

Pentru a declara o clasă ca fiind sigilată, inserăm sigilate modificator înainte de clasă modificator în antetul declarației de clasă - în cazul nostru, am declarat Formă clasa ca sigilate. O clasă sigilată este incompletă fără subclasele sale - la fel ca o clasă abstractă tipică - deci trebuie să declare subclasele individuale din același fișier (shape.kt în acest caz). Rețineți că nu puteți defini o subclasă a unei clase sigilate dintr-un alt fișier. 

În codul de mai sus, am specificat că Formă clasa poate fi extinsă doar de clase CercTriunghi, și Dreptunghi.

Clasele închise în Kotlin au următoarele reguli suplimentare:

  • Putem adăuga modificatorul abstract la o clasă sigilată, dar acest lucru este redundant, deoarece clasele sigilate sunt în mod implicit abstracte.
  • Clasele închise nu pot avea deschis sau final modificator. 
  • De asemenea, suntem liberi să declarăm clasele de date și obiectele ca subclase într-o clasă sigilată (acestea trebuie încă declarate în același fișier). 
  • Clasele închise nu au voie să aibă constructori publici - constructorii lor sunt privați în mod implicit. 

Clasele care extind subclasele unei clase închise pot fi plasate fie în același fișier, fie într-un alt fișier. Subclasa de clasă închisă trebuie marcată cu deschis modificator (veți afla mai multe despre moștenire în Kotlin în postul următor). 

// employee.kt clasa sigilată Angajat clasă deschisă Artist: Angajat () // class musician.kt Muzician: Artist ()

O clasă sigilată și subclasele sale sunt într-adevăr la îndemână cand expresie. De exemplu:

distracție whatIsIt (formă: Shape) = atunci când (forma) este cerc -> println ("un cerc") este triunghi -> println ("triunghi"

Aici compilatorul este inteligent pentru a ne asigura că am acoperit toate posibilitățile cand cazuri. Aceasta înseamnă că nu este nevoie să adăugați altfel clauză. 

Dacă ar fi trebuit să facem în schimb următoarele:

distracție whatIsIt (forma: Shape) = atunci când (forma) este cerc -> println ("un cerc") este triunghi -> println ("triunghi"

Codul nu s-ar compila, pentru că nu am inclus toate cazurile posibile. Am avea următoarea eroare:

Kotlin: expresia "atunci când" trebuie să fie exhaustivă, adăugați necesară o ramură dreptunghiulară sau o ramură "altceva".

Deci, am putea să includem și este dreptunghi caz sau includeți altfel clauză pentru a finaliza cand expresie. 

Concluzie

În acest tutorial, ați aflat mai multe despre cursurile din Kotlin. Am abordat următoarele despre proprietățile clasei:

  • întârzierea inițializării
  • inline properties 
  • proprietăți de extindere

De asemenea, ați învățat despre câteva clase cool și avansate, cum ar fi date, enum, imbricate și clase sigilate. În următorul tutorial din seria Kotlin From Scratch, veți fi introduși în interfețe și moștenire în Kotlin. Ne vedem în curând!

Pentru a afla mai multe despre limba Kotlin, vă recomand să vizitați documentația Kotlin. Sau verificați câteva dintre celelalte postări pentru dezvoltarea aplicațiilor Android aici pe Envato Tuts!

Cod