Kotlin er et moderne programmeringsspråk som kompilerer til Java bytecode. Det er gratis og åpen kildekode, og lover å gjøre koding for Android enda morsommere.
I forrige artikkel lærte du om klasser og objekter i Kotlin. I denne opplæringen fortsetter vi å lære mer om egenskaper og se også på avanserte typer klasser i Kotlin ved å utforske følgende:
Vi kan erklære en ikke-null eiendom i Kotlin som sen-initialisert. Dette betyr at en ikke-null-egenskap ikke vil bli initialisert på utsagnstidspunktet med en verdi-faktisk initialisering vil ikke skje via noen konstruktør-men i stedet vil den bli sent initialisert ved en metode eller avhengighetsinjeksjon.
La oss se på et eksempel for å forstå denne unike egenskapsmodifikatoren.
klasse Presenter private var depot: Repository? = null morsom initRepository (repo: Repository): Enhet this.repository = repo klassen Repository fun saveAmount (beløp: Double)
I koden ovenfor erklærte vi en mutable nullable oppbevaringssted
eiendom som er av typen Oppbevaringssted
-inne i klassen Presenter
-og vi så initialiserte denne egenskapen til null under erklæring. Vi har en metode initRepository ()
i Presenter
klasse som reinitializes denne egenskapen senere med en faktisk Oppbevaringssted
forekomst. Merk at denne egenskapen også kan tilordnes en verdi ved hjelp av en avhengighetsinjektor som Dagger.
Nå for oss å påberope seg metoder eller egenskaper på dette oppbevaringssted
eiendom, vi må gjøre en nullkontroll eller bruk den sikre anropsoperatøren. Hvorfor? Fordi det oppbevaringssted
eiendommen er av nullable type (Oppbevaringssted?
). (Hvis du trenger en oppdatering om nullabilitet i Kotlin, vennligst besøk Nullability, Loops og Conditions).
// Inside Presenter klassen morsom lagring (beløp: Dobbel) depot?. SaveAmount (beløp)
For å unngå å gjøre null sjekker hver gang vi trenger å påberope en eiendom metode, kan vi markere den egenskapen med lateinit
modifikator-dette betyr at vi har erklært den egenskapen (som er en forekomst av en annen klasse) som sen-initialisert (som betyr at eiendommen vil bli initialisert senere).
klassespresentator private lateinit var repository: Repository // ...
Nå, så lenge vi venter til eiendommen har fått en verdi, er vi trygge for å få tilgang til egenskapens metoder uten å foreta null kontroller. Eiendomsinitialiseringen kan skje enten i en setter-metode eller gjennom avhengighetsinjeksjon.
repository.saveAmount (mengde)
Merk at hvis vi prøver å få tilgang til metoder for eiendommen før den er initialisert, vil vi få en kotlin.UninitializedPropertyAccessException
i stedet for en NullPointerException
. I dette tilfellet vil unntaksmeldingen være "Lateinit Property Repository har ikke blitt initialisert".
Legg også merke til følgende restriksjoner angitt når du forsinker en egenskapsinitialisering med lateinit
:
Var
).int
, Dobbelt
, Flyte
, og så videre. I avanserte funksjoner introduserte jeg på linje
modifikator for høyere rekkefølgefunksjoner-dette hjelper til med å optimalisere eventuelle høyere rekkefølgefunksjoner som aksepterer en lambda som en parameter.
I Kotlin kan vi også bruke dette på linje
modifikator på egenskaper. Bruk av denne modifikatoren vil optimalisere tilgangen til eiendommen.
La oss se et praktisk eksempel.
klasse student val nickName: String get () println ("Nick navn hentet") returnere "koloCoder" morsom hoved (args: Array) val student = Student () print (student.nickName)
I koden ovenfor har vi en normal eiendom, kallenavn
, det har ikke den på linje
modifier. Hvis vi dekompilerer kodestykket, bruker du Vis Kotlin Bytecode funksjonen (hvis du er i IntelliJ IDEA eller Android Studio, bruk Verktøy > Kotlin > Vis Kotlin Bytecode), ser vi følgende Java-kode:
offentlig siste klasse Student @NotNull offentlig endelig String getNickName () String var1 = "Nick navn hentet"; System.out.println (var1); returnere "koloCoder"; offentlig endelig klasse InlineFunctionKt offentlig statisk sluttbrudd hoved (@NotNull String [] args) Intrinsics.checkParameterIsNotNull (args, "args"); Student student = ny student (); String var2 = student.getNickName (); System.out.print (var2);
I den genererte Java-koden ovenfor (noen elementer i den genererte koden ble fjernet for korthetens skyld), kan du se det inne i hoved()
Metoden kompilatoren opprettet en Student
objekt, kalt getNickName ()
metode, og deretter trykt returverdi.
La oss nå angi egenskapen som på linje
i stedet, og sammenlign den genererte bytekoden.
// ... inline val nickName: String // ...
Vi setter bare inn på linje
modifikator før variabelmodifieringsenheten: Var
eller val
. Her er bytekoden generert for denne inline-egenskapen:
// ... offentlig statisk sluttbrudd hoved (@NotNull String [] args) Intrinsics.checkParameterIsNotNull (args, "args"); Student student = ny student (); String var3 = "Nick navn hentet"; System.out.println (var3); String var2 = "koloCoder"; System.out.print (var2); // ...
Igjen ble noen kode fjernet, men det viktigste å merke seg er hoved()
metode. Kompilatoren har kopiert eiendommen få()
fungere kropp og limte den inn i anropssiden (denne mekanismen ligner på inline-funksjoner).
Vår kode er optimalisert på grunn av at du ikke trenger å opprette et objekt og ringe egenskapen getter-metoden. Men som diskutert i inlinefunksjonene, ville vi ha en større bytekode enn før, så bruk med forsiktighet.
Merk også at denne mekanismen vil fungere for egenskaper som ikke har et bakfelt (husk, et bakfelt er bare et felt som brukes av egenskaper når du vil endre eller bruke feltdataene).
I avanserte funksjoner diskuterte jeg også utvidelsesfunksjoner - disse gir oss muligheten til å forlenge en klasse med ny funksjonalitet uten å arve fra den klassen. Kotlin gir også en lignende mekanisme for egenskaper, som kalles utvidelsesegenskaper.
val String.upperCaseFirstLetter: String get () = this.substring (0, 1) .toUpperCase () .pluss (this.substring (1))
I posten avanserte funksjoner definerte vi en uppercaseFirstLetter ()
utvidelsesfunksjon med mottaker type string
. Her har vi konvertert den til en utvidelsesegenskap på toppnivå i stedet. Merk at du må definere en getter-metode på eiendommen din for at dette skal fungere.
Så med denne nye kunnskapen om utvidelsesegenskaper, vil du vite at hvis du noen gang ønsket at en klasse skulle ha en eiendom som ikke var tilgjengelig, er du fri til å lage en forlengelseseiendom av den klassen.
La oss starte med en typisk Java-klasse eller POJO (vanlig gammelt Java-objekt).
offentlig klasse BlogPost privat endelig String title; privat endelig URI url; privat endelig String beskrivelse; privat endelig dato publisering dato // ... konstruktør ikke inkludert for korthetens skyld @Override public boolean equals (Objekt o) hvis (dette == o) returner sann; hvis (o == null || getClass ()! = o.getClass ()) returnere false; BlogPost blogPost = (BlogPost) o; hvis (tittel! = null?! title.equals (blogPost.title): blogPost.title! = null) return false; hvis (url! = null?! url.equals (blogPost.url): blogPost.url! = null) returner false; hvis (beskrivelse! = 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; resultat = 31 * resultat + (url! = null? url.hashCode (): 0); resultat = 31 * resultat + (beskrivelse! = null? description.hashCode (): 0); result = 31 * result + (publishDate! = null? publishDate.hashCode (): 0); returresultat; @Override public String toString () return "BlogPost " + "title = '" + tittel +' \ "+", url = "+ url +", + beskrivelse + "\" + ", publishDate =" + publishDate + ''; // ... setters og getters ignoreres også for korthetens skyld
Som du kan se, må vi eksplisitt kode klassen eiendomsaksessorer: getter og setter, så vel som hashCode
, er lik
, og toString
metoder (selv om IntelliJ IDEA, Android Studio eller AutoValue-biblioteket kan hjelpe oss med å generere dem). Vi ser denne typen boilerplate-kode for det meste i datalaget av et typisk Java-prosjekt. (Jeg fjernet feltaksessorene og konstruktøren for korthetens skyld).
Den kule tingen er at Kotlin-teamet ga oss med data
modifikator for klasser for å eliminere skrive disse boilerplate.
La oss nå skrive den forrige koden i Kotlin i stedet.
dataklasse BlogPost (var tittel: String, var url: URI, var beskrivelse: String, var publishDate: Date)
Rått! Vi spesifiserer bare data
modifikator før klasse
søkeord for å lage en dataklasse - akkurat som det vi gjorde i vår Blogg innlegg
Kotlin klasse over. Nå er lik
, hashCode
, toString
, kopiere
, og flere komponentmetoder vil bli opprettet under hetten for oss. Merk at en dataklasse kan utvide andre klasser (dette er en ny funksjon i Kotlin 1.1).
er lik
MetodeDenne metoden sammenligner to objekter for likestilling, og returnerer sant hvis de er lik eller falske ellers. Med andre ord sammenligner det om de to klassetilfellene inneholder de samme dataene.
student.equals (student3) // ved hjelp av == i Kotlin student == student3 // samme som å bruke likeverdige ()
I Kotlin bruker du likestillingsoperatøren ==
vil ringe er lik
metode bak kulissene.
hashCode
Metode Denne metoden returnerer en heltallverdi som brukes til rask lagring og gjenfinning av data lagret i en hashbasert samlingsdatastruktur, for eksempel i HashMap
og HashSet
samlingstyper.
toString
MetodeDenne metoden returnerer a string
representasjon av et objekt.
dataklasse Person (var førstnavn: String, var lastnavn: String) val person = Person ("Chike", "Mgbemena") println (person) // utskrifter "Person (firstName = Chike, lastName = Mgbemena)"
Ved å bare ringe til klassetilfellet, får vi en strengobjekt som er returnert til oss - Kotlin kaller objektet toString ()
under hetten for oss. Men hvis vi ikke legger data
søkeord i, se hva vår objekt streng representasjon ville være:
com.chike.kotlin.classes.Person@2f0e140b
Mye mindre informativ!
kopiere
MetodeDenne metoden lar oss lage en ny forekomst av et objekt med alle samme egenskapsverdier. Med andre ord oppretter det en kopi av objektet.
val person1 = Person ("Chike", "Mgbemena") println (person1) // Person (firstName = Chike, lastName = Mgbemena) val person2 = person1.copy () println (person2) // Person (firstName = Chike, lastName = Mgbemena)
En kul ting om kopiere
Metoden i Kotlin er evnen til å endre egenskaper under kopiering.
val person3 = person1.copy (lastName = "Onu") println (person3) // Person3 (firstName = Chike, lastName = Onu)
Hvis du er en Java-koder, ligner denne metoden på klone ()
metode du allerede er kjent med. Men Kotlin kopiere
Metoden har kraftigere funksjoner.
I Person
klasse, har vi også to metoder automatisk generert for oss av kompilatoren på grunn av data
søkeord plassert i klassen. Disse to metodene er prefixed med "komponent", deretter etterfulgt av et tall suffiks: component1 ()
, component2 ()
. Hver av disse metodene representerer de enkelte egenskapene til typen. Merk at suffikset tilsvarer rekkefølgen av egenskapene deklarert i den primære konstruktøren.
Så, i vårt eksempel kalles component1 ()
vil returnere fornavnet og ringe component2 ()
vil returnere etternavnet.
println (person3.component1 ()) // Chike println (person3.component2 ()) // Onu
Å ringe egenskapene ved hjelp av denne stilen er vanskelig å forstå og lese, men så kaller eiendomsnavnet eksplisitt mye bedre. Imidlertid har disse implisitt opprettede egenskapene en svært nyttig formål: de la oss gjøre en destruktivdeklarasjon, der vi kan tilordne hver komponent til en lokal variabel.
val (firstName, lastName) = Person ("Angelina", "Jolie") println (firstName + "" + LastName) // Angelina Jolie
Det vi har gjort her er å direkte tildele første og andre egenskaper (fornavn
og etternavn
) av Person
skriv til variablene fornavn
og etternavn
henholdsvis. Jeg diskuterte også denne mekanismen kjent som destruktivdeklarasjon i den siste delen av pakken og grunnleggende funksjoner posten.
I posten Mer moro med funksjoner fortalte jeg deg at Kotlin har støtte for lokale eller nestede funksjoner - en funksjon som er deklarert i en annen funksjon. Vel, Kotlin støtter på samme måte nestede klasser-en klasse opprettet i en annen klasse.
klasse OuterClass klasse NestedClass fun nestedClassFunc ()
Vi kaller til og med den nestede klassens offentlige funksjoner som vist nedenfor - en nestet klasse i Kotlin tilsvarer a statisk
nestet klasse i Java. Legg merke til at nestede klasser ikke kan lagre en referanse til deres ytre klasse.
val nestedClass = OuterClass.NestedClass () nestedClass.nestedClassFunc ()
Vi kan også sette den nestede klassen som privat - det betyr at vi bare kan lage en forekomst av NestedClass
innenfor rammen av OuterClass
.
Inner klasser kan derimot referere til den ytre klassen den ble erklært i. For å lage en indre klasse plasserer vi indre
søkeord før klasse
søkeord i en nestet klasse.
klasse OuterClass () val oCPropt: String = "Yo" indre klasse InnerClass morsom indreClassFunc () val outerClass = denne @ OuterClass-utskriften (outerClass.oCPropt) // utskrifter "Yo"
Her refererer vi til OuterClass
fra InnerClass
ved bruk av denne @ OuterClass
.
Enum-typen erklærer et sett med konstanter som er representert av identifikatorer. Denne spesielle typen klasse er opprettet av søkeordet enum
som er spesifisert før klasse
søkeord.
enum klasse Land NIGERIA, GHANA, CANADA
For å hente en enverdien basert på navnet sitt (akkurat som i Java) gjør vi dette:
Country.valueOf ( "Nigeria")
Eller vi kan bruke Kotlin enumValueOf
hjelpemetode for å få tilgang til konstanter på en generisk måte:
enumValueOf( "Nigeria")
Også, vi kan få alle verdiene (som for en Java enum) slik:
Country.values ()
Til slutt kan vi bruke Kotlin EnumValue
hjelpemetode for å få alle enumoppføringer på en generisk måte:
EnumValue()
Dette returnerer en matrise som inneholder ENUM-oppføringene.
Akkurat som en normal klasse, den enum
typen kan ha sin egen konstruktør med egenskaper knyttet til hver enum konstant.
enum klasse Land (val callingCode: Int) NIGERIA (234), USA (1), GHANA (233)
I Land
enum type primærkonstruktor, definerte vi den uforanderlige egenskapen callingCodes
for hver enum konstant. I hver av konstantene ga vi et argument til konstruktøren.
Vi kan da få tilgang til fastighetsegenskapen slik:
val land = Country.NIGERIA print (country.callingCode) // 234
En forseglet klasse i Kotlin er en abstrakt klasse (du har aldri tenkt å lage gjenstander fra den) hvilke andre klasser kan utvides. Disse underklassene er definert inne i det forseglede klassekroppen-i samme fil. Fordi alle disse underklassene er definert i det forseglede klassekroppen, kan vi kjenne alle mulige underklasser ved bare å se filen.
La oss se et praktisk eksempel.
// shape.kt forseglet klasse Form Klasse Sirkel: Form () Klasse Triangel: Form () Klasse Rektangel: Form ()
For å erklære en klasse som forseglet, setter vi inn forseglet
modifikator før klasse
modifikator i klassedeklarasjonen header-i vårt tilfelle, erklærte vi Form
klasse som forseglet
. En forseglet klasse er ufullstendig uten sine underklasser - akkurat som en typisk abstrakt klasse - så vi må deklare de enkelte underklassene i samme fil (shape.kt i dette tilfellet). Merk at du ikke kan definere en underklasse av en forseglet klasse fra en annen fil.
I koden ovenfor har vi angitt at Form
klassen kan kun utvides av klassene Sirkel
, Triangel
, og Rektangel
.
Forseglede klasser i Kotlin har følgende tilleggsregler:
abstrakt
til en forseglet klasse, men dette er overflødig fordi forseglede klasser er abstrakte som standard.åpen
eller endelig
modifier. Klasser som utvider underklasser av en forseglet klasse, kan plasseres enten i samme fil eller en annen fil. Den forseglede klasseklassen må merkes med åpen
modifikator (du vil lære mer om arv i Kotlin i neste innlegg).
// employee.kt forseglet klasse Medarbeider åpen klasse Artist: Medarbeider () // musician.kt klasse Musiker: Artist ()
En forseglet klasse og dens underklasser er veldig nyttige i en når
uttrykk. For eksempel:
morsomt hvaIsIt (form: form) = når (form) er sirkel -> println ("en sirkel") er trekant -> println ("en trekant") er rektangel -> println ("et rektangel")
Her er kompilatoren smart for å sikre at vi dekket alt mulig når
saker. Det betyr at det ikke er nødvendig å legge til ellers
klausul.
Hvis vi skulle gjøre følgende i stedet:
moro hvaIsIt (form: form) = når (form) er sirkel -> println ("en sirkel") er trekant -> println ("en trekant")
Koden ville ikke kompilere, fordi vi ikke har tatt med alle mulige tilfeller. Vi har følgende feil:
Kotlin: 'når' uttrykket må være uttømmende, legg til nødvendig 'er rektangel' grenen eller 'else' grenen i stedet.
Så vi kunne enten inkludere er rektangel
tilfelle eller inkludere ellers
klausul for å fullføre når
uttrykk.
I denne opplæringen lærte du mer om klasser i Kotlin. Vi dekket følgende om klassegenskaper:
Du lærte også om noen kule og avanserte klasser som data, enum, nestede og forseglede klasser. I neste opplæring i Kotlin From Scratch-serien blir du introdusert til grensesnitt og arv i Kotlin. Ser deg snart!
For å lære mer om Kotlin-språket, anbefaler jeg at du besøker Kotlin-dokumentasjonen. Eller sjekk ut noen av våre andre Android-apputviklingsposter her på Envato Tuts!