Utvidelsesmetoder i Kotlin

1. Introduksjon

Kotlin introduserer konseptet Extension Methods - som er en praktisk måte å utvide eksisterende klasser med en ny funksjonalitet uten å bruke arv eller noen former for dekoratørmønsteret - etter å ha definert en utvidelse. vi kan egentlig bruke den - som den var delen av den opprinnelige API-en.

Dette kan være veldig nyttig for å gjøre koden vår enkel å lese og vedlikeholde, ettersom vi kan legge til metoder som er spesifikke for våre behov og at de ser ut til å være en del av den opprinnelige koden, selv når vi ikke har tilgang til kildene.

For eksempel kan det hende vi må utføre XML-rømning på en String. I standard Java-kode trenger vi å skrive en metode som kan utføre dette og kalle det:

Streng rømt = escapeStringForXml (input);

Mens det er skrevet i Kotlin, kan kodebiten erstattes med:

val escaped = input.escapeForXml ()

Ikke bare er dette lettere å lese, men IDE vil kunne tilby metoden som et autofullføringsalternativ det samme som om det var en standardmetode på String klasse.

2. Standard utvidelsesmetoder for biblioteker

Kotlin Standard Library leveres med utvidelsesmetoder utenom boksen.

2.1. Kontekstjusterende utvidelsesmetoder

Noen generiske utvidelser finnes og kan brukes på alle typer i applikasjonen vår. Disse kan brukes til å sikre at koden kjøres i en passende sammenheng, og i noen tilfeller for å sikre at en variabel ikke er null.

Det viser seg at vi mest sannsynlig utnytter utvidelser uten å innse dette.

En av de mest populære er muligens la() metode, som kan kalles på hvilken som helst type i Kotlin - la oss gi en funksjon til den som vil bli utført på den opprinnelige verdien:

val name = "Baeldung" val store bokstaver = name .let {n -> n.toUpperCase ()}

Det ligner på kart() metode fra Valgfri eller Strøm klasser - i dette tilfellet passerer vi en funksjon som representerer en handling som konverterer en gitt String inn i den øvre casing-representasjonen.

Variabelen Navn er kjent som mottakeren av samtalen fordi det er variabelen som utvidelsesmetoden virker på.

Dette fungerer bra med operatøren for sikker samtale:

val name = maybeGetName () val uppercase = name? .let {n -> n.toUpperCase ()}

I dette tilfellet passerte blokken til la() blir bare evaluert hvis variabelen Navn var ikke-null. Dette betyr at inne i blokken, verdien n er garantert ikke-null. Mer om dette her.

Det er andre alternativer til la() det kan også være nyttig, avhengig av våre behov.

De løpe() utvidelse fungerer det samme som la(), men en mottaker er gitt som dette verdi inne i den kalt blokken:

val name = "Baeldung" val versal = name.run {toUpperCase ()}

søke om() fungerer det samme som løpe(), men den returnerer en mottaker i stedet for å returnere verdien fra den angitte blokken.

La oss dra nytte av søke om() til kjederelaterte samtaler:

val languages ​​= mutableListOf () languages.apply {add ("Java") add ("Kotlin") add ("Groovy") add ("Python")} .apply {remove ("Python")} 

Legg merke til hvordan koden vår blir mer kortfattet og uttrykksfull uten å måtte bruke det eksplisitt dette eller den.

Også () utvidelse fungerer akkurat som la(), men den returnerer mottakeren på samme måte som søke om() gjør:

val languages ​​= mutableListOf () languages.also {list -> list.add ("Java") list.add ("Kotlin") list.add ("Groovy")} 

De takeIf () utvidelse er utstyrt med et predikat som virker på mottakeren, og hvis dette predikatet returnerer ekte så returnerer den mottakeren eller null ellers - dette fungerer på samme måte som en kombinasjon av et felles kart() og filter() metoder:

val språk = getLanguageUsed () val coolLanguage = språk.takeIf {l -> l == "Kotlin"} 

TakeUnless () utvidelse er den samme som takeIf () men med den omvendte predikatlogikken.

val språk = getLanguageUsed () val oldLanguage = språk.takeUnless {l -> l == "Kotlin"} 

2.2. Utvidelsesmetoder for samlinger

Kotlin legger til et stort antall utvidelsesmetoder til standard Java Collections som kan gjøre koden vår enklere å jobbe med.

Disse metodene er plassert inne _Collections.kt, _Ranges.kt, og _Sequences.kt, i tillegg til _Arrays.kt for tilsvarende metoder å bruke på Arrays i stedet. (Husk at i Kotlin, Arrays kan behandles på samme måte som Samlinger)

Det er altfor mange av disse utvidelsesmetodene å diskutere her, så ta en titt på disse filene for å se hva som er tilgjengelig.

I tillegg til samlinger legger Kotlin et betydelig antall utvidelsesmetoder til String klasse - definert i _Strings.kt. Disse tillater oss å behandle Strenger som om de var samlinger av tegn.

Alle disse utvidelsesmetodene jobber sammen for å tillate oss å skrive betydelig renere, enklere å vedlikeholde koden uavhengig av hvilken type samling vi jobber med.

3. Skrive våre utvidelsesmetoder

Så hva om vi trenger å utvide en klasse med en ny funksjonalitet - enten fra Java eller Kotlin Standard Library eller fra et avhengig bibliotek som vi bruker?

Forlengelsesmetoder skrives som enhver annen metode, men mottakerklassen er gitt som en del av funksjonsnavnet, atskilt med perioden.

For eksempel:

morsom String.escapeForXml (): String {....}

Dette vil definere en ny funksjon som kalles escapeForXml som en utvidelse til String klasse, slik at vi kan kalle det som beskrevet ovenfor.

Inne i denne funksjonen kan vi få tilgang til mottakeren ved hjelp av dette, det samme som om vi hadde skrevet dette inne i String klassen selv:

morsom String.escapeForXml (): String {returner dette .replace ("&", "&") .replace ("<", "", ">")}

3.1. Skrive generiske utvidelsesmetoder

Hva om vi ønsker å skrive en utvidelsesmetode som er ment å brukes på flere typer, generelt? Vi kan bare utvide Noen type, - som tilsvarer Gjenstand klasse i Java - men det er en bedre måte.

Utvidelsesmetoder kan brukes på både en generisk mottaker og en konkret:

moro T.concatAsString (b: T): String {return this.toString () + b.toString ()}

Dette kan brukes på alle typer som oppfyller de generiske kravene, og inne i funksjonen dette verdien er typesafe.

For eksempel ved å bruke eksemplet ovenfor:

5.concatAsString (10) // kompilerer "5" .concatAsString ("10") // kompilerer 5.concatAsString ("10") // kompilerer ikke

3.2. Skrive Infix-utvidelsesmetoder

Infix-metoder er nyttige for å skrive DSL-stilkode, ettersom de gjør det mulig å kalle metoder uten punktum eller parentes:

infix fun Number.toPowerOf (eksponent: Number): Double {return Math.pow (this.toDouble (), exponent.toDouble ())}

Vi kan nå kalle dette det samme som alle andre infixmetoder:

3 toPowerOf 2 // 9 9 toPowerOf 0.5 // 3

3.3. Skrivemetoder for utvidelse av operatører

Vi kan også skrive en operatørmetode som en utvidelse.

Operatørmetoder er de som lar oss dra nytte av operatørens stenografi i stedet for hele metodens navn - f.eks i tillegg til operatørmetoden kan kalles ved hjelp av + operatør:

operator moro List.times (av: Int): List {return this.map {it * by}}

Igjen fungerer dette på samme måte som andre operatørmetoder:

listOf (1, 2, 3) * 4 // [4, 8, 12]

4. Kaller Kotlin Extension-funksjon fra Java

La oss nå se hvordan Java opererer med Kotlin-utvidelsesfunksjoner.

Generelt er hver utvidelsesmetode vi definerer i Kotlin tilgjengelig for oss å bruke i Java. Vi bør imidlertid huske at infix metoden må fremdeles kalles med prikk og parentes. Samme med operatørutvidelser - vi kan ikke bare bruke plusstegnet (+). Disse fasilitetene er bare tilgjengelige i Kotlin.

Imidlertid kan vi ikke kalle noen av de vanlige Kotlin-biblioteksmetodene i Java, som la eller søke om, fordi de er merket med @InlineOnly.

4.1. Synlighet av den tilpassede utvidelsesfunksjonen i Java

La oss bruke en av de tidligere definerte utvidelsesfunksjonene - String.escapeXml (). Filen vår som inneholder utvidelsesmetoden heter StringUtil.kt.

Nå, når vi trenger å ringe en utvidelsesmetode fra Java, må vi bruke et klassenavn StringUtilKt. Merk at vi må legge til Kt suffiks:

String xml = "hei"; String escapedXml = StringUtilKt.escapeForXml (xml); assertEquals ("hei", escapedXml);

Vær oppmerksom på den første escapeForXml parameter. Dette tilleggsargumentet er en mottaker av utvidelsesfunksjon. Kotlin med utvidelsesfunksjon på toppnivå er en ren Java klasse med en statisk metode. Derfor må den på en eller annen måte passere originalen String.

Og selvfølgelig, akkurat som i Java, vi kan bruke statisk import:

importer statisk com.baeldung.kotlin.StringUtilKt. *;

4.2. Kaller en innebygd Kotlin-utvidelsesmetode

Kotlin hjelper oss med å skrive kode enklere og raskere ved å tilby mange innebygde utvidelsesfunksjoner. For eksempel er det String.kapitaliser () metode, som kan kalles direkte fra Java:

String name = "john"; Streng kapitalisert navn = StringsKt.capitalize (navn); assertEquals ("John", store bokstaver);

Derimot, vi kan ikke kalle utvidelsesmetoder merket med @InlineOnly fra Java, for eksempel:

inline fun T.let (blokk: (T) -> R): R.

4.3. Gi nytt navn til den genererte Java Static Class

Vi vet allerede at en Kotlin utvidelsesfunksjon er en statisk Java metode. La oss gi nytt navn til en generert Java klasse med en kommentar @fil: JvmName (navn: streng).

Dette må legges til øverst i filen:

@fil: JvmName ("Strings") pakke com.baeldung.kotlin moro String.escapeForXml (): String {returner denne .replace ("&", "&") .replace ("<", "", ">" )}

Nå, når vi vil kalle en utvidelsesmetode, trenger vi bare å legge til Strenger klassenavn:

Strings.escapeForXml (xml);

Vi kan fortsatt legge til en statisk import:

importer statisk com.baeldung.kotlin.Strings. *;

5. Sammendrag

Utvidelsesmetoder er nyttige verktøy for å utvide typer som allerede eksisterer i systemet - enten fordi de ikke har den funksjonaliteten vi trenger, eller bare for å gjøre et bestemt kodeområde lettere å administrere.

Vi har her sett noen utvidelsesmetoder som er klare til bruk i systemet. I tillegg undersøkte vi forskjellige muligheter for utvidelsesmetoder. Noen eksempler på denne funksjonaliteten finner du på GitHub.


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