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 pakker og grunnleggende funksjoner i Kotlin. Funksjoner er i hjertet av Kotlin, så i dette innlegget vil vi se nærmere på dem. Vi undersøker følgende typer funksjoner i Kotlin:
Du vil bli overrasket over alle de kule tingene du kan gjøre med funksjoner i Kotlin!
Funksjoner på toppnivå er funksjoner i en Kotlin-pakke som er definert utenfor klassen, objektet eller grensesnittet. Dette betyr at de er funksjoner du ringer direkte, uten å måtte opprette noen objekter eller ringe noen klasser.
Hvis du er en Java-koder, vet du at vi vanligvis lager verktøy statiske metoder i hjelperklasser. Disse hjelperklassene gjør egentlig ikke noe - de har ingen tilstands- eller instansmetoder, og de fungerer bare som en beholder for de statiske metodene. Et typisk eksempel er samlinger
klasse i java.util
pakke og dens statiske metoder.
Toppnivåfunksjoner i Kotlin kan brukes som erstatning for de statiske verktøyene i hjelperkategorier vi kodes i Java. La oss se på hvordan du definerer en toppnivåfunksjon i Kotlin.
pakke com.chikekotlin.projectx.utils sjekk sjekkUserStatus (): String return "online"
I koden ovenfor definerte vi en pakke com.chikekotlin.projectx.utils
inne i en fil som heter UserUtils.kt og definerte også en toppnøkkelfunksjon som kalles checkUserStatus ()
inne i samme pakke og fil. For korthetens skyld returnerer denne svært enkle funksjonen strengen "online".
Den neste tingen vi skal gjøre er å bruke denne funksjonen i en annen pakke eller fil.
pakke com.chikekotlin.projectx.users importere com.chikekotlin.projectx.utils.checkUserStatus if (checkUserStatus () == "online") // gjør noe
I den forrige koden importerte vi funksjonen til en annen pakke og deretter utført den! Som du kan se trenger vi ikke å opprette et objekt eller referere til en klasse for å ringe denne funksjonen.
Gitt at Java ikke støtter toppnivåfunksjoner, vil Kotlin-kompilatoren bak kulissene opprette en Java-klasse, og de enkelte toppnivåfunksjonene blir konvertert til statiske metoder. I vårt eget tilfelle var Java-klassen generert UserUtilsKt
med en statisk metode checkUserStatus ()
.
/ * Java * / pakke com.chikekotlin.projectx.utils offentlig klasse UserUtilsKt offentlig statisk String checkUserStatus () return "online";
Dette betyr at Java-oppringere bare kan kalle metoden ved å referere til den genererte klassen, akkurat som for enhver annen statisk metode.
/ * Java * / import com.chikekotlin.projectx.utils.UserUtilsKt ... UserUtilsKt.checkUserStatus ()
Vær oppmerksom på at vi kan endre Java-klassenavnet som Kotlin-kompilatoren genererer ved å bruke @JvmName
merknad.
@file: JvmName ("UserUtils") pakke com.chikekotlin.projectx.utils morsom sjekkeStatusStatus (): String return "online"
I koden ovenfor brukte vi @JvmName
annotasjon og angitt et klassenavn UserUtils
for den genererte filen. Merk også at denne merknaden er plassert i begynnelsen av Kotlin-filen, før pakkedefinisjonen.
Det kan refereres fra Java slik:
/ * Java * / import com.chikekotlin.projectx.utils.UserUtils ... UserUtils.checkUserStatus ()
Lambda-uttrykk (eller funksjonsbokstavene) er heller ikke bundet til noen enhet som en klasse, objekt eller grensesnitt. De kan overføres som argumenter til andre funksjoner som kalles høyereordningsfunksjoner (vi diskuterer disse i neste innlegg). Et lambda-uttrykk representerer bare en blokk av en funksjon, og ved å bruke dem reduseres støyen i vår kode.
Hvis du er en Java-koder, vet du at Java 8 og nyere gir støtte til lambda-uttrykk. For å bruke lambda-uttrykk i et prosjekt som støtter tidligere Java-versjoner som Java 7, 6 eller 5, kan vi bruke det populære Retrolambda-biblioteket.
En av de fantastiske tingene om Kotlin er at lambda-uttrykkene støttes ut av boksen. Fordi lambda ikke støttes i Java 6 eller 7, for Kotlin å interoperere med det, oppretter Kotlin en Java-anonym klasse bak scenen. Men vær oppmerksom på at å lage et lambda-uttrykk i Kotlin er ganske annerledes enn det er i Java.
Her er kjennetegnene til et lambda-uttrykk i Kotlin:
.moro
søkeord. ()
. Og dessuten kan vi tilordne et lambda-uttrykk til en variabel og deretter utføre den.
La oss nå se noen eksempler på lambda-uttrykk. I koden under opprettet vi et lambda-uttrykk uten noen parametre og tildelte det en variabel budskap
. Vi kjørte da lambda-uttrykket ved å ringe budskap()
.
val message = println ("Hei, Kotlin er veldig kul!") melding () // "Hei, Kotlin er veldig kul!"
La oss også se hvordan å legge inn parametere i et lambda-uttrykk.
val message = myString: String -> println (myString) melding ("Jeg elsker Kotlin") // "Jeg elsker Kotlin" melding ("Hvor langt?") // "Hvor langt?"
I koden ovenfor opprettet vi et lambda-uttrykk med parameteren myString
, sammen med parameter typen string
. Som du kan se, foran parameter typen, er det en pil: dette refererer til lambda kroppen. Med andre ord, separerer denne pilen parameterlisten fra lambda-kroppen. For å gjøre det mer konsistent, kan vi helt ignorere parametertypen (allerede utledet av kompilatoren).
val message = myString -> println (myString) // vil fortsatt kompilere
For å ha flere parametere, skiller vi dem bare med komma. Og husk, vi bryter ikke parameterlisten i parentes som i Java.
val addNumbers = number1: Int, number2: Int -> println ("Legge til $ number1 og $ number2") valresultat = number1 + number2 println ("Resultatet er $ result") addNumbers (1, 3)
Vær imidlertid oppmerksom på at hvis parametertypene ikke kan utledes, må de spesifiseres eksplisitt (som i dette eksemplet), ellers vil koden ikke kompilere.
Legge til 1 og 3 Resultatet er 4
Vi kan sende lambda-uttrykk som parametre til funksjoner: disse kalles "høyere-order-funksjoner", fordi de er funksjoner av funksjoner. Disse funksjonene kan akseptere en lambda eller en anonym funksjon som parameter: for eksempel, siste()
samlingsfunksjon.
I koden nedenfor passerte vi i et lambda-uttrykk til siste()
funksjon. (Hvis du vil ha en oppfriskning på samlinger i Kotlin, besøk den tredje opplæringen i denne serien) Som navnet sier, returnerer det siste elementet i listen. siste()
aksepterer et lambda-uttrykk som en parameter, og dette uttrykket tar i sin tur ett argument av typen string
. Funksjonsorganet fungerer som et predikat for å søke i en delmengde av elementer i samlingen. Det betyr at lambda-uttrykket vil bestemme hvilke elementer i samlingen som skal vurderes når man ser etter den siste.
val strengliste: Liste= listOf ("in", "the", "club") utskrift (stringList.last ()) // vil skrive ut "club" utskrift (stringList.last (s: String -> s.length == 3) ) // vil skrive ut "the"
La oss se hvordan du gjør den siste kodelinjen over mer lesbar.
stringList.last s: String -> s.length == 3 // vil også kompilere og skrive ut "the"
Kotlin-kompilatoren gjør det mulig for oss å fjerne funksjonal parentes hvis det siste argumentet i funksjonen er et lambda-uttrykk. Som du kan observere i koden ovenfor, fikk vi lov til å gjøre dette fordi det siste og eneste argumentet passerte til siste()
funksjon er et lambda-uttrykk.
Videre kan vi gjøre det mer konsistent ved å fjerne parametertypen.
stringList.last s -> s.length == 3 // vil også kompilere utskrift "the"
Vi trenger ikke spesifisere parameter typen eksplisitt, fordi parameter typen er alltid den samme som samling element type. I koden ovenfor ringer vi siste
på en liste samling av string
objekter, så Kotlin-kompilatoren er smart nok til å vite at parameteren også vil være a string
type.
den
ArgumentnavnVi kan til og med forenkle lambda-uttrykket ytterligere på nytt ved å erstatte lambda-uttrykksargumentet med det automatisk genererte standardargumentnavnet den
.
stringList.last it.length == 3
De den
Argumentnavn ble automatisk generert fordi siste
kan akseptere et lambda-uttrykk eller en anonym funksjon (vi kommer til det kort) med bare ett argument, og dens type kan utledes av kompilatoren.
La oss starte med et eksempel. I koden nedenfor passerer vi et lambda-uttrykk til for hver()
funksjon påkalt på intList
samling. Denne funksjonen løper gjennom samlingen og utfører lambda på hvert element i listen. Hvis et element er delbart med 2, vil det stoppe og returnere fra lambda.
morsom surroundingFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach if (it% 2 == 0) return println ("End of surroundingFunction ()") surroundingFunction ) // ingenting skjedde
Å kjøre koden ovenfor har kanskje ikke gitt deg det resultatet du kanskje har forventet. Dette skyldes at returmeldingen ikke kommer tilbake fra lambdaen, men i stedet fra den inneholdende funksjonen surroundingFunction ()
! Dette betyr at den siste kodenetningen i surroundingFunction ()
vil ikke utføre.
// ... println ("End of surroundingFunction ()") // Dette vil ikke utføre // ...
For å fikse dette problemet må vi fortelle det eksplisitt hvilken funksjon å returnere fra ved å bruke en etikett eller navnemerke.
morsom surroundingFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach hvis (det% 2 == 0) return @ forEach println ("End of surroundingFunction ()") / / Nå vil det utføre surroundingFunction () // print "End of surroundingFunction ()"
I den oppdaterte koden ovenfor angav vi standardmerket @for hver
umiddelbart etter komme tilbake
søkeord inne i lambda. Vi har nå instruert kompilatoren til å returnere fra lambda i stedet for den inneholdende funksjonen surroundingFunction ()
. Nå den siste uttalelsen av surroundingFunction ()
vil utføre.
Merk at vi også kan definere vår egen etikett eller navnemerke.
// ... intList.forEk myLabel @ if (it% 2 == 0) return @ myLabel // ...
I koden ovenfor definerte vi vår egendefinerte etikett som heter mylabel @
og deretter spesifisert den for komme tilbake
søkeord. De @for hver
etikett generert av kompilatoren for for hver
funksjonen er ikke lenger tilgjengelig fordi vi har definert vår egen.
Men du vil snart se hvordan dette lokale returproblemet kan løses uten etiketter når vi diskuterer anonyme funksjoner i Kotlin snart.
Denne typen funksjon er definert i en klasse, objekt eller grensesnitt. Ved hjelp av medlemsfunksjoner kan vi modulere våre programmer videre. La oss nå se hvordan du oppretter en medlemsfunksjon.
klassen sirkel morsom beregningArea (radius: dobbelt): Dobbelt krever (radius> 0, "Radius må være større enn 0") returner Math.PI * Math.pow (radius, 2.0)
Denne kodestykket viser en klasse Sirkel
(vi diskuterer Kotlin-klasser i senere innlegg) som har en medlemsfunksjon calculateArea ()
. Denne funksjonen tar en parameter radius
å beregne området av en sirkel.
For å påberope en medlemsfunksjon bruker vi navnet på den inneholdende klassen eller objektet forekomsten med en prikk, etterfulgt av funksjonsnavnet, og sender eventuelle argumenter om nødvendig.
val sirkel = Sirkel () utskrift (circle.calculateArea (4.5)) // vil skrive ut "63.61725123519331"
En anonym funksjon er en annen måte å definere en blokk med kode som kan overføres til en funksjon. Det er ikke bundet til noen identifikator. Her er kjennetegnene til en anonym funksjon i Kotlin:
moro
søkeordval strengliste: Liste= listOf ("in", "the", "club") print (stringList.last it.length == 3) // vil skrive ut "the"
Fordi vi passerte en lambda til siste()
funksjonen ovenfor, kan vi ikke være eksplisitte om returtypen. For å være eksplisitt om returtypen, må vi bruke en anonym funksjon i stedet.
val strLenThree = stringList.last (morsom (streng): Boolsk return string.length == 3) print (strLenThree) // vil skrive ut "the"
I ovennevnte kode har vi erstattet lambda-uttrykket med en anonym funksjon fordi vi ønsker å være eksplisitt om returtypen.
Mot slutten av lambda-delen i denne opplæringen brukte vi en etikett for å spesifisere hvilken funksjon som skal returneres fra. Bruk en anonym funksjon i stedet for en lambda inne i for hver()
funksjonen løser dette problemet enklere. Returtrykket returnerer fra den anonyme funksjonen og ikke fra den omliggende, som i vårt tilfelle er surroundingFunction ()
.
morsomt rundtFunksjon () val intList = listOf (1, 2, 3, 4, 5) intList.forEach (morsomt (tall) hvis (tall% 2 == 0) return) println ("End of surroundingFunction ) ") // setning utført surroundingFunction () // vil skrive ut" End of surroundingFunction () "
For å ta programmodularisering videre, gir Kotlin oss lokale funksjoner - også kjent som nestede funksjoner. En lokal funksjon er en funksjon som er deklarert i en annen funksjon.
morsomt printCircumferenceAndArea (radius: Double): Enhet morsom calCircumference (radius: Double): Dobbel = (2 * Math.PI) * radiusval circumference = "% .2f" .format (calCircumference (radius)) Double): Double = (Math.PI) * Math.pow (radius, 2.0) val area = "% .2f" .format (calArea (radius)) print ("Sirkelomkretsen på $ radiusradius er $ omkrets og område er $ område ") printCircumferenceAndArea (3.0) // Sirkelomkretsen på 3,0 radius er 18,85 og området er 28,27
Som du kan observere i kodestykket ovenfor, har vi to enkeltlinjefunksjoner: calCircumference ()
og calArea ()
nestet inne i printCircumferenceAndAread ()
funksjon. De nestede funksjonene kan bare kalles fra innsiden av funksjonen og ikke utenfor. Igjen, bruk av nestede funksjoner gjør vårt program mer modulært og ryddig.
Vi kan gjøre våre lokale funksjoner mer konsise ved ikke å eksplisitt sende parametere til dem. Dette er mulig fordi lokale funksjoner har tilgang til alle parametere og variabler av omsluttingsfunksjonen. La oss se det nå i aktion:
morsom printCircumferenceAndArea (radius: Double): Enhet morsom calCircumference (): Dobbel = (2 * Math.PI) * radius val circumference = "% .2f" .format (calCircumference ()) morsom calArea (): Double = .PI) * Math.pow (radius, 2,0) val område = "% .2f" .format (calArea ()) // ...
Som du kan se, ser denne oppdaterte koden mer lesbar og reduserer støyen vi hadde før. Selv om omsluttingsfunksjonen i dette eksemplet er liten, i en større omslutningsfunksjon som kan brytes ned i mindre nestede funksjoner, kan denne funksjonen virkelig komme til nytte.
De infiks
notat gjør det mulig for oss å enkelt ringe en en-medlems funksjon eller utvidelsesfunksjon. I tillegg til at en funksjon er ett argument, må du også definere funksjonen ved hjelp av infiks
modifier. For å opprette en infix-funksjon er to parametre involvert. Den første parameteren er målobjektet, mens den andre parameteren bare er en enkelt parameter som sendes til funksjonen.
La oss se på hvordan du lager en infix-funksjon i en klasse. I kodeseksempelet nedenfor opprettet vi en Student
klasse med en mutable kotlinScore
eksempel felt. Vi opprettet en infix-funksjon ved å bruke infiks
modifikator før moro
søkeord. Som du ser nedenfor, opprettet vi en infix-funksjon addKotlinScore ()
som tar en score og legger til kotlinScore
forekomstfelt.
klasse Student var kotlinScore = 0.0 infix moro addKotlinScore (poengsum: Dobbel): Enhet this.kotlinScore = kotlinScore + score
La oss også se hvordan du bruker den infix-funksjonen vi har opprettet. For å ringe en infix-funksjon i Kotlin trenger vi ikke å bruke punktnotasjonen, og vi trenger ikke å vikle parameteren med parenteser.
val student = Student () student addKotlinScore 95,00 print (student.kotlinScore) // vil skrive ut "95.0"
I koden ovenfor kalte vi infix-funksjonen, målobjektet er student
, og den doble 95.00
er parameteren gått til funksjonen.
Ved hjelp av infix-funksjoner kan vi gjøre koden mer uttrykksfulle og klarere enn den vanlige stilen. Dette er høyt verdsatt når du skriver enhetstester i Kotlin (vi diskuterer testing i Kotlin i et fremtidig innlegg).
"Chike" burde starte med ("ch") myList skal inneholde (myElement) "Chike" should haveLength (5) myMap skal haKey (myKey)
til
Infix-funksjonI Kotlin kan vi lage en Par
forekommer mer kortfattet ved å bruke til
infix funksjon i stedet for Par
konstruktør. (Bak scenen, til
skaper også en Par
eksempel.) Merk at til
funksjon er også en utvidelsesfunksjon (vi diskuterer disse i neste innlegg).
offentlig infix moro A.to (at: B): Par = Par (dette, det)
La oss nå sammenligne opprettelsen av a Par
eksempel ved bruk av begge til
infix funksjon og direkte bruk av Par
konstruktør, som utfører samme operasjon, og se hvilken som er bedre.
val nigeriaCallingCodePair = 234 til "Nigeria" val nigeriaCallingCodePair2 = Par (234, "Nigeria") // Samme som ovenfor
Som du kan se i koden ovenfor, bruker du til
infix-funksjonen er mer konsis enn direkte ved bruk av Par
konstruktør for å lage en Par
forekomst. Husk at du bruker til
infix funksjon, 234
er målobjektet og string
"Nigeria" er parameteren overført til funksjonen. Legg merke til at vi også kan gjøre dette for å lage en Par
type:
val nigeriaCallingCodePair3 = 234.to ("Nigeria") // samme som å bruke 234 til "Nigeria"
I Ranges and Collections posten opprettet vi en kartsamling i Kotlin ved å gi den en liste over par - den første verdien er nøkkelen, og den andre verdien. La oss også sammenligne opprettelsen av et kart ved å bruke begge til
infix funksjon og Par
konstruktør for å lage de enkelte parene.
val callingCodesMap: Kart= mapOf (234 til "Nigeria", 1 til "USA", 233 til "Ghana")
I koden ovenfor opprettet vi en kommaseparert liste over Par
typer som bruker til
infix funksjon og sendt dem til mapOf ()
funksjon. Vi kan også lage det samme kartet ved å bruke Par
konstruktør for hvert par.
val callingCodesPairMap: Kart= mapOf (Par (234, "Nigeria"), Par (1, "USA"), Par (233, "Ghana"))
Som du kan se igjen, stikker du med til
infix-funksjonen har mindre lyd enn å bruke Par
konstruktør.
I denne opplæringen lærte du om noen av de kule tingene du kan gjøre med funksjoner i Kotlin. Vi dekket:
Men det er ikke alt! Det er fortsatt mer å lære om funksjoner i Kotlin. Så i neste innlegg lærer du noen avanserte bruksområder av funksjoner, for eksempel utvidelsesfunksjoner, høyere rekkefølgefunksjoner og nedleggelser. 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+!