Lambda Expressions i Kotlin

1. Oversikt

I denne artikkelen skal vi utforske Lambdas på Kotlin-språket. Husk at lambdas ikke er unike for Kotlin og har eksistert i mange år på mange andre språk.

Lambdas-uttrykk er egentlig anonyme funksjoner som vi kan behandle som verdier - vi kan for eksempel sende dem som argumenter til metoder, returnere dem eller gjøre andre ting vi kan gjøre med et vanlig objekt.

2. Definere en Lambda

Som vi får se, er Kotlin Lambdas veldig lik Java Lambdas. Du kan finne ut mer om hvordan du arbeider med Java Lambdas og noen gode fremgangsmåter her.

For å definere en lambda, må vi holde oss til syntaksen:

val lambdaName: Type = {argumentList -> codeBody}

Den eneste delen av en lambda som ikke er valgfri er codeBody.

Argumentlisten kan hoppes over når man maksimalt definerer ett argument og Type kan ofte utledes av Kotlin-kompilatoren. Vi trenger ikke alltid en variabel også, lambda kan sendes direkte som et metodeargument.

Typen av den siste kommandoen i en lambda-blokk er den returnerte typen.

2.1. Skriv inferens

Kotlins typeavledning gjør at typen lambda kan vurderes av kompilatoren.

Å skrive en lambda som produserer kvadratet til et tall, ville være som skrevet som:

val kvadrat = {nummer: Int -> nummer * nummer} val ni = kvadrat (3)

Kotlin vil evaluere eksemplet ovenfor for å være en funksjon som tar en Int og returnerer en Int:(Int) -> Int

Hvis vi ønsket å lage en lambda som multipliserer de enkelte argumenttallene med 100, returnerer den verdien som en Streng:

val magnitude100String = {input: Int -> val magnitude = input * 100 magnitude.toString ()} 

Kotlin vil forstå at denne lambdaen er av typen (Int) -> String.

2.2. Type erklæring

Noen ganger kan ikke Kotlin utlede våre typer, og vi må eksplisitt erklære typen for vår lambda; akkurat som vi kan med alle andre typer.

Mønsteret er inngang -> utgangMen hvis koden ikke gir noen verdi, bruker vi typen Enhet:

val at: Int -> Int = {three -> three}
val more: (String, Int) -> String = {str, int -> str + int}
val noReturn: Int -> Unit = {num -> println (num)}

Vi kan bruke lambdas som klasseutvidelser:

val another: String. (Int) -> String = {this + it}

Mønsteret vi bruker her er litt annerledes enn de andre lambdas vi har definert. Våre parenteser inneholder fremdeles argumentene våre, men før parentesene våre har vi typen vi skal feste denne lambdaen til.

For å bruke dette mønsteret fra en String vi kaller Type.lambdaName (argumenter)så for å kalle vårt ‘et annet’ eksempel:

moro utvideString (arg: String, num: Int): String {val another: String. (Int) -> String = {this + it} return arg.another (num)}

2.3. Tilbake fra en Lambda

Det endelige uttrykket er verdien som returneres etter at en lambda er utført:

val calculateGrade = {grade: Int -> when (grade) {in 0..40 -> "Fail" in 41..70 -> "Pass" in 71..100 -> "Distinction" else -> false}}

Den siste måten er å utnytte den anonyme funksjonsdefinisjonen - vi må definere argumentene og returtypen eksplisitt og kan bruke returuttalelsen på samme måte som hvilken som helst metode:

val calculateGrade = fun (grade: Int): String {if (grade 100) {return "Error"} else if (grade <40) {return "Fail"} else if (grade <70) {return "Pass"} return "Distinction"}

3. den

En stenografi av et enkelt argument lambda er å bruke nøkkelordet ‘den'. Denne verdien representerer ethvert eneste argumentet vi overfører til lambda-funksjonen.

Vi skal utføre det samme for hver metode på følgende utvalg av Ints:

val array = arrayOf (1, 2, 3, 4, 5, 6)

Vi vil først se på langhåndsformen til lambda-funksjonen, etterfulgt av stenografisk form av samme kode, der ‘den'Vil representere hvert element i den følgende matrisen.

Langhånd:

array.forEach {item -> println (item * 4)}

Forkortelse:

array.forEach {println (it * 4)}

4. Implementering av Lambdas

Vi vil veldig kort dekke hvordan du kaller en lambda som er i omfang, samt hvordan du skal passere en lambda som et argument.

Når et lambda-objekt er innenfor omfanget, kan du kalle det som en hvilken som helst annen metode for bruk, ved hjelp av navnet fulgt av parenteser og eventuelle argumenter:

morsom påkalleLambda (lambda: (Double) -> Boolean): Boolean {return lambda (4.329)}

Hvis vi trenger å gi en lambda som et argument til en høyere ordensmetode, har vi fem alternativer.

4.1. Lambda Objektvariabel

Ved å bruke et eksisterende lambda-objekt som erklært i avsnitt 2, sender vi objektet inn i metoden som vi hadde gjort med ethvert annet argument:

@Test fun whenPassingALambdaObject_thenCallTriggerLambda () {val lambda = {arg: Double -> arg == 4.329} val result = invokeLambda (lambda) assertTrue (result)}

4.2. Lambda bokstavelig

I stedet for å tilordne lambda til en variabel, kan vi overføre bokstavelig direkte til metodeanropet:

Test moro nårPassingALambdaLiteral_thenCallTriggerLambda () {val result = invokeLambda ({true}) assertTrue (result)}

4.3. Lambda bokstavelig utenfor brakettene

Et annet mønster for lambda-bokstaver oppmuntret av JetBrains - er å sende lambda inn som det siste argumentet til en metode og plassere lambda utenfor metoden:

@Test fun whenPassingALambdaLiteralOutsideBrackets_thenCallTriggerLambda () {val result = invokeLambda {arg -> arg.isNaN ()} assertFalse (result)}

4.4. Metode Referanser

Til slutt har vi muligheten til å bruke metodereferanser. Dette er referanser til eksisterende metoder.

I eksemplet vårt nedenfor tar vi Dobbelt :: isFinite. Denne funksjonen får deretter den samme strukturen som en lambda, men den er av typen KFunksjon1 som det har ett argument, tar inn en Dobbelt og returnerer a Boolsk:

@Test fun whenPassingAFunctionReference_thenCallTriggerLambda () {val reference = Double :: isFinite val result = invokeLambda (reference) assertTrue (result)}

5. Kotlin Lambda på Java

Kotlin bruker genererte funksjonsgrensesnitt for å interopere med Java. De finnes i Kotlin-kildekoden her.

Vi har en begrensning på antall argumenter som kan sendes inn med disse genererte klassene. Nåværende grense er 22; representert av grensesnittet Funksjon22.

Strukturen til en Funksjon grensesnittets generiske er at tallet og representerer antall argumenter for lambda, så vil det antall klasser være argumentet Typer i rekkefølge.

Det endelige generiske argumentet er returtypen:

import kotlin.jvm.functions. * public interface Function1: Function {public operator fun invoke (p1: P1): R}

Når det ikke er definert noen returtype innenfor Kotlin-koden, returnerer lambda en Kotlin Enhet. Java-koden må importere klassen fra kotlin pakke og returner med null.

Nedenfor er et eksempel på å kalle en Kotlin Lambda fra et prosjekt som er del Kotlin og delvis Java:

importere kotlin.Unit; importer kotlin.jvm.functions.Function1; ... ny Funksjon1 () {@ Override offentlig enhet påkalle (kunde c) {AnalyticsManager.trackFacebookLogin (c.getCreated ()); return null; }} 

Når du bruker Java8, bruker vi en Java lambda i stedet for en Funksjon anonym klasse:

@Test ugyldig gittJava8_whenUsingLambda_thenReturnLambdaResult () {assertTrue (LambdaKt.takeLambda (c -> c> = 0)); }

6. Anonyme indre klasser

Kotlin har to interessante måter å jobbe med anonyme indre klasser på.

6.1. Objektuttrykk

Når vi kaller en Kotlin Inner Anonymous Class eller en Java Anonymous Class som består av flere metoder, må vi implementere et objektuttrykk.

For å demonstrere dette tar vi et enkelt grensesnitt og en klasse som tar en implementering av det grensesnittet og kaller metodene avhengig av en Boolsk argument:

klasse Prosessor {grensesnitt ActionCallback {morsom suksess (): String fun failure (): String} fun performEvent (avgjørelse: Boolsk, tilbakeringing: ActionCallback): String {return hvis (beslutning) {callback.success ()} ellers {callback.failure ()}}}

Nå for å gi en anonym indre klasse, må vi bruke syntaksen "objekt":

@Test fun givenMultipleMethods_whenCallingAnonymousFunction_thenTriggerSuccess () {val result = Processor (). PerformEvent (true, object: Processor.ActionCallback {override fun success () = "Success" override fun failure () = "Failure"}) assertEquals ("Success", resultat) }

6.2. Lambda-uttrykk

På den annen side kan vi også ha muligheten til å bruke en lambda i stedet. Å bruke lambdas i stedet for en anonym indre klasse har visse betingelser:

  1. Klassen er en implementering av et Java-grensesnitt (ikke et Kotlin-en)
  2. grensesnittet må ha maks

Hvis begge disse vilkårene er oppfylt, kan vi bruke et lambdauttrykk i stedet.

Lambda i seg selv vil ta så mange argumenter som grensesnittets enkle metode gjør.

Et vanlig eksempel er å bruke en lambda i stedet for en standard Java Forbruker:

val list = ArrayList (2) list.stream () .forEach ({i -> println (i)})

7. Konklusjon

Mens syntaktisk like, er Kotlin og Java lambdas helt forskjellige funksjoner. Når du målretter mot Java 6, må Kotlin forvandle sine lambdas til en struktur som kan brukes i JVM 1.6.

Til tross for dette gjelder fortsatt beste praksis for Java 8 lambdas.

Mer om best practices for lambda her.

Kodebiter, som alltid, finnes på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found