Lambda-udtryk i Kotlin

1. Oversigt

I denne artikel skal vi udforske Lambdas på Kotlin-sproget. Husk, at lambdas ikke er unikke for Kotlin og har eksisteret i mange år på mange andre sprog.

Lambdas-udtryk er i det væsentlige anonyme funktioner, som vi kan behandle som værdier - vi kan for eksempel videregive dem som argumenter til metoder, returnere dem eller gøre andre ting, vi kunne gøre med et normalt objekt.

2. Definition af en Lambda

Som vi får se, ligner Kotlin Lambdas meget Java Lambdas. Du kan finde ud af mere om, hvordan du arbejder med Java Lambdas og nogle bedste fremgangsmåder her.

For at definere en lambda skal vi holde os til syntaksen:

val lambdaName: Type = {argumentList -> codeBody}

Den eneste del af en lambda, som ikke er valgfri, er codeBody.

Argumentlisten kan springes over, når der højst defineres et argument og Type kan ofte udledes af Kotlin-kompilatoren. Vi har ikke altid brug for en variabel så godt, lambda kan sendes direkte som et metodeargument.

Typen af ​​den sidste kommando inden for en lambda-blok er den returnerede type.

2.1. Type inferens

Kotlins type inferens gør det muligt at evaluere typen af ​​en lambda af compileren.

At skrive en lambda, der producerer kvadratet af et tal, ville være som skrevet som:

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

Kotlin vil evaluere ovenstående eksempel for at være en funktion, der tager en Int og returnerer en Int:(Int) -> Int

Hvis vi ønskede at oprette en lambda, der multiplicerer sine enkelte argumenttal med 100, returnerer denne værdi som en Snor:

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

Kotlin vil forstå, at denne lambda er af typen (Int) -> String.

2.2. Type erklæring

Lejlighedsvis kan Kotlin ikke udlede vores typer, og vi skal udtrykkeligt erklære typen for vores lambda; ligesom vi kan med enhver anden type.

Mønsteret er input -> outputdog hvis koden ikke returnerer nogen værdi, bruger vi typen Enhed:

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

Vi kan bruge lambdas som klasseudvidelser:

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

Det mønster, vi bruger her, er lidt anderledes end de andre lambdas, vi har defineret. Vores parenteser indeholder stadig vores argumenter, men før vores parenteser har vi den type, som vi vil knytte denne lambda til.

At bruge dette mønster fra en Snor vi kalder Type.lambdaName (argumenter)så for at kalde vores 'andet' eksempel:

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

2.3. Vender tilbage fra en Lambda

Det endelige udtryk er den værdi, der returneres, efter at en lambda er udført:

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

Den sidste måde er at udnytte den anonyme funktionsdefinition - vi skal definere argumenterne og returtypen eksplicit og kan bruge returudtalelsen på samme måde som enhver metode:

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

3. det

En stenografi af et enkelt argument lambda er at bruge nøgleordet 'det'. Denne værdi repræsenterer ethvert ensomt argument, som vi videregiver til lambda-funktionen.

Vi udfører det samme for hver metode på følgende matrix af Ints:

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

Vi ser først på den langhåndede form af lambda-funktionen efterfulgt af stenografi af den samme kode, hvor 'det'Repræsenterer hvert element i det følgende array.

Langhånd:

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

Forkortelse:

array.forEach {println (it * 4)}

4. Implementering af Lambdas

Vi vil meget kort dække, hvordan man kalder en lambda, der er inden for omfanget, samt hvordan man sender en lambda som et argument.

Når et lambda-objekt er inden for omfanget, skal du kalde det som enhver anden metode inden for anvendelsesområdet ved hjælp af dets navn efterfulgt af parenteser og eventuelle argumenter:

sjov påkaldLambda (lambda: (Double) -> Boolean): Boolean {return lambda (4.329)}

Hvis vi har brug for at videregive en lambda som et argument til en højere ordens metode, har vi fem muligheder.

4.1. Lambda-objektvariabel

Ved hjælp af et eksisterende lambda-objekt som erklæret i afsnit 2 sender vi objektet ind i metoden som vi ville med ethvert andet argument:

@Test sjov nårPasserALambdaObject_thenCallTriggerLambda () {val lambda = {arg: Double -> arg == 4.329} val result = invokeLambda (lambda) assertTrue (result)}

4.2. Lambda bogstavelig

I stedet for at tildele lambda til en variabel kan vi overføre den bogstavelige direkte til metodekaldet:

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

4.3. Lambda bogstaveligt uden for beslagene

Et andet mønster for lambda-bogstaver opmuntret af JetBrains - er at føre lambda ind som det sidste argument til en metode og placere lambda uden for metodekaldet:

@Test sjovt, når du passererALambdaLiteralOutsideBrackets_thenCallTriggerLambda () {val result = invokeLambda {arg -> arg.isNaN ()} assertFalse (result)}

4.4. Metode Referencer

Endelig har vi muligheden for at bruge metodereferencer. Dette er henvisninger til eksisterende metoder.

I vores eksempel nedenfor tager vi Dobbelt :: isFinite. Denne funktion får derefter den samme struktur som en lambda, men den er af typen KFunktion1 da det har et argument, tager det ind a Dobbelt og returnerer a Boolsk:

@Test sjov nårPassingAFunctionReference_thenCallTriggerLambda () {val reference = Double :: isFinite val result = invokeLambda (reference) assertTrue (result)}

5. Kotlin Lambda i Java

Kotlin bruger genererede funktionsgrænseflader til at interopere med Java. De findes i Kotlin-kildekoden her.

Vi har en grænse for antallet af argumenter, der kan overføres til disse genererede klasser. Den nuværende grænse er 22; repræsenteret af grænsefladen Funktion22.

Opbygningen af ​​en Fungere interface's generiske er, at antallet og repræsenterer antallet af argumenter for lambda, så vil antallet af klasser være argumentet Typer i rækkefølge.

Det sidste generiske argument er returtypen:

import kotlin.jvm.funktioner. * offentlig grænseflade Funktion1: Funktion {offentlig operatør sjov påkald (p1: P1): R}

Når der ikke er defineret nogen returtype inden for Kotlin-koden, returnerer lambda en Kotlin Enhed. Java-koden skal importere klassen fra kotlin pakke og returnere med nul.

Nedenfor er et eksempel på at kalde en Kotlin Lambda fra et projekt, der er del Kotlin og del Java:

import kotlin.Unit; import kotlin.jvm.functions.Function1; ... ny Funktion1 () {@ Override offentlig enhed påkalder (kunde c) {AnalyticsManager.trackFacebookLogin (c.getCreated ()); returnere null; }} 

Når vi bruger Java8, bruger vi en Java lambda i stedet for en Fungere anonym klasse:

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

6. Anonyme indre klasser

Kotlin har to interessante måder at arbejde med anonyme indre klasser på.

6.1. Objektudtryk

Når vi kalder en Kotlin Inner Anonym Class eller en Java Anonym Class bestående af flere metoder, skal vi implementere et objektudtryk.

For at demonstrere dette tager vi en simpel grænseflade og en klasse, der tager en implementering af grænsefladen og kalder metoderne afhængige af en Boolsk argument:

klasse Processor {interface ActionCallback {fun success (): String fun fail (): String} fun performEvent (beslutning: Boolsk, tilbagekald: ActionCallback): String {return hvis (beslutning) {callback.success ()} ellers {callback.failure ()}}}

For at give en anonym indre klasse er vi nødt til at bruge syntaksen "objekt":

@Test sjov givenMultipleMethods_whenCallingAnonymousFunction_thenTriggerSuccess () {val result = Processor (). PerformEvent (true, object: Processor.ActionCallback {override fun success () = "Succes" tilsidesætter fun fail () = "Failure"}) assertEquals ("Success", resultat) }

6.2. Lambda-udtryk

På den anden side har vi muligvis også mulighed for at bruge en lambda i stedet. Brug af lambdas i stedet for en anonym indre klasse har visse betingelser:

  1. Klassen er en implementering af en Java-grænseflade (ikke en Kotlin-en)
  2. grænsefladen skal have maks

Hvis begge disse betingelser er opfyldt, kan vi bruge et lambda-udtryk i stedet.

Lambda selv tager så mange argumenter som grænsefladesens enkle metode gør.

Et almindeligt eksempel ville være at bruge en lambda i stedet for en standard Java Forbruger:

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

7. Konklusion

Mens syntaktisk ens, er Kotlin og Java lambdas helt forskellige funktioner. Når der målrettes mod Java 6, skal Kotlin omdanne sine lambdas til en struktur, der kan bruges inden for JVM 1.6.

På trods af dette gælder den bedste praksis for Java 8 lambdas stadig.

Mere om lambda's bedste praksis her.

Kodestykker kan som altid findes på GitHub.


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