Udvidelsesmetoder i Kotlin

1. Introduktion

Kotlin introducerer begrebet Extension Methods - som er en praktisk måde at udvide eksisterende klasser med en ny funktionalitet uden at bruge arv eller nogen form for dekoratørmønster - efter at have defineret en udvidelse. vi kan i det væsentlige bruge det - da det var den del af den oprindelige API.

Dette kan være meget nyttigt for at gøre vores kode let at læse og vedligeholde, da vi er i stand til at tilføje metoder, der er specifikke for vores behov og få dem til at se ud til at være en del af den oprindelige kode, selv når vi ikke har adgang til kilderne.

For eksempel er vi muligvis nødt til at udføre XML-undslip på en Snor. I standard Java-kode skal vi skrive en metode, der kan udføre dette og kalde det:

Streng undsluppet = escapeStringForXml (input);

Mens skrevet i Kotlin, kunne uddraget erstattes med:

val escaped = input.escapeForXml ()

Dette er ikke kun lettere at læse, men IDE'er vil kunne tilbyde metoden som en autofuldførelsesmulighed den samme som om det var en standardmetode på Snor klasse.

2. Standardmetoder til biblioteksudvidelse

Kotlin Standard Library leveres med nogle udvidelsesmetoder uden for kassen.

2.1. Kontekstjusterende udvidelsesmetoder

Nogle generiske udvidelser findes og kan anvendes på alle typer i vores applikation. Disse kan bruges til at sikre, at koden køres i en passende sammenhæng, og i nogle tilfælde for at sikre, at en variabel ikke er nul.

Det viser sig, at vi sandsynligvis udnytter udvidelser uden at indse dette.

En af de mest populære er muligvis lade() metode, som kan kaldes til enhver type i Kotlin - lad os give en funktion til den, der udføres på den oprindelige værdi:

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

Det ligner kort() metode fra Valgfri eller Strøm klasser - i dette tilfælde passerer vi en funktion, der repræsenterer en handling, der konverterer en given Snor ind i dets fremhævede repræsentation.

Variablen navn er kendt som modtageren af ​​opkaldet fordi det er den variabel, som udvidelsesmetoden virker på.

Dette fungerer godt med den sikre opkaldsoperatør:

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

I dette tilfælde gik blokken videre til lade() evalueres kun, hvis variablen navn var ikke-nul. Dette betyder, at inde i blokken, værdien n er garanteret ikke-nul. Mere om dette her.

Der er andre alternativer til lade() det kan dog også være nyttigt afhængigt af vores behov.

Det løb() udvidelse fungerer det samme som lade(), men en modtager leveres som det her værdi inde i den kaldte blok:

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

ansøge() fungerer det samme som løb(), men det returnerer en modtager i stedet for at returnere værdien fra den angivne blok.

Lad os drage fordel af ansøge() til kæderelaterede opkald:

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

Læg mærke til, hvordan vores kode bliver mere kortfattet og udtryksfuld uden at skulle bruge det eksplicit det her eller det.

Også () udvidelse fungerer ligesom lade(), men det returnerer modtageren på samme måde som ansøge() gør:

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

Det takeIf () udvidelse er forsynet med et predikat, der virker på modtageren, og hvis dette predikat vender tilbage rigtigt så returnerer den modtageren eller nul Ellers fungerer dette på samme måde som en kombination af et fælles kort() og filter() metoder:

val sprog = getLanguageUsed () val coolLanguage = sprog.takeIf {l -> l == "Kotlin"} 

TakeUnless () udvidelse er den samme som takeIf () men med den omvendte prædikatlogik.

val sprog = getLanguageUsed () val oldLanguage = sprog.takeUnless {l -> l == "Kotlin"} 

2.2. Udvidelsesmetoder til samlinger

Kotlin tilføjer et stort antal udvidelsesmetoder til standard Java Collections, som kan gøre vores kode lettere at arbejde med.

Disse metoder er placeret indeni _Collections.kt, _Ranges.ktog _Sequences.kt, såvel som _Arrays.kt for ækvivalente metoder at anvende på Arrays i stedet. (Husk at i Kotlin, Arrays kan behandles det samme som Samlinger)

Der er alt for mange af disse udvidelsesmetoder til at diskutere her, så kig gennem disse filer for at se, hvad der er tilgængeligt.

Ud over samlinger tilføjer Kotlin et betydeligt antal udvidelsesmetoder til Snor klasse - defineret i _Strings.kt. Disse giver os mulighed for at behandle Strenge som om de var samlinger af tegn.

Alle disse udvidelsesmetoder arbejder sammen for at give os mulighed for at skrive betydeligt renere, lettere at vedligeholde kode uanset hvilken slags samling vi arbejder med.

3. Skrivning af vores udvidelsesmetoder

Så hvad hvis vi har brug for at udvide en klasse med en ny funktionalitet - enten fra Java eller Kotlin Standard Library eller fra et afhængigt bibliotek, som vi bruger?

Udvidelsesmetoder skrives som enhver anden metode, men modtagerklassen leveres som en del af funktionsnavnet, adskilt med perioden.

For eksempel:

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

Dette vil definere en ny funktion kaldet escapeForXml som en udvidelse til Snor klasse, så vi kan kalde det som beskrevet ovenfor.

Inde i denne funktion kan vi få adgang til modtageren ved hjælp af det her, det samme som om vi havde skrevet dette inde i Snor klasse selv:

sjov String.escapeForXml (): Streng {returner denne .replace ("&", "&") .replace ("<", "", ">")}

3.1. Skrivning af generiske udvidelsesmetoder

Hvad hvis vi ønsker at skrive en udvidelsesmetode, der er beregnet til at blive anvendt på flere typer, generelt? Vi kunne bare udvide Nogen type, - hvilket svarer til Objekt klasse i Java - men der er en bedre måde.

Udvidelsesmetoder kan anvendes på en generisk modtager såvel som en konkret:

sjov T.concatAsString (b: T): String {returner this.toString () + b.toString ()}

Dette kan anvendes på enhver type, der opfylder de generiske krav og inden i funktionen det her værdi er typesafe.

For eksempel ved hjælp af ovenstående eksempel:

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

3.2. Skrivning af Infix-udvidelsesmetoder

Infix-metoder er nyttige til at skrive DSL-stilkode, da de giver mulighed for at kalde metoder uden punktum eller parenteser:

infix sjovt Number.toPowerOf (eksponent: Number): Dobbelt {returner Math.pow (this.toDouble (), exponent.toDouble ())}

Vi kan nu kalde dette det samme som enhver anden infix-metode:

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

3.3. Skrivningsudvidelsesmetoder til operatører

Vi kunne også skrive en operatørmetode som en udvidelse.

Operatørmetoder er dem, der giver os mulighed for at drage fordel af operatørens stenografi i stedet for det fulde metodenavn - f.eks plus operatørmetoden kan kaldes ved hjælp af + operatør:

operatør sjov List.times (af: Int): List {return this.map {it * by}}

Igen fungerer dette det samme som enhver anden operatørmetode:

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

4. Opkald til Kotlin-udvidelsesfunktion fra Java

Lad os nu se, hvordan Java fungerer med Kotlin-udvidelsesfunktioner.

Generelt er enhver udvidelsesmetode, vi definerer i Kotlin, tilgængelig for os til brug i Java. Vi skal dog huske, at infix metode skal stadig kaldes med prik og parentes. Samme med operatorudvidelser - vi kan ikke kun bruge plustegnet (+). Disse faciliteter er kun tilgængelige i Kotlin.

Vi kan dog ikke kalde nogle af de almindelige Kotlin-biblioteksmetoder i Java, som f.eks lade eller ansøge, fordi de er markeret med @InlineOnly.

4.1. Synlighed af den tilpassede udvidelsesfunktion i Java

Lad os bruge en af ​​de tidligere definerede udvidelsesfunktioner - String.escapeXml (). Vores fil, der indeholder udvidelsesmetoden, kaldes StringUtil.kt.

Når vi nu skal kalde en udvidelsesmetode fra Java, skal vi bruge et klassenavn StringUtilKt. Bemærk, at vi skal tilføje Kt suffiks:

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

Vær opmærksom på den første escapeForXml parameter. Dette yderligere argument er en udvidelsesfunktion modtager type. Kotlin med udvidelsesfunktion på øverste niveau er en ren Java klasse med en statisk metode. Derfor skal det på en eller anden måde passere originalen Snor.

Og selvfølgelig, ligesom i Java, vi kan bruge statisk import:

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

4.2. Opkald til en indbygget Kotlin-udvidelsesmetode

Kotlin hjælper os med at skrive kode lettere og hurtigere ved at levere mange indbyggede udvidelsesfunktioner. For eksempel er der Snor.kapitaliser () metode, som kan kaldes direkte fra Java:

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

Imidlertid, vi kan ikke kalde udvidelsesmetoder markeret med @InlineOnly fra Java, for eksempel:

inline sjov T.let (blok: (T) -> R): R.

4.3. Omdøbning af den genererede Java Static Class

Vi ved allerede, at en Kotlin udvidelsesfunktion er en statisk Java metode. Lad os omdøbe en genereret Java klasse med en kommentar @fil: JvmName (navn: streng).

Dette skal tilføjes øverst i filen:

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

Når vi nu vil kalde en udvidelsesmetode, skal vi blot tilføje Strenge klasse navn:

Strings.escapeForXml (xml);

Vi kan også stadig tilføje en statisk import:

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

5. Resume

Udvidelsesmetoder er nyttige værktøjer til at udvide typer, der allerede findes i systemet - enten fordi de ikke har den funktionalitet, vi har brug for, eller blot for at gøre et specifikt kodeområde lettere at administrere.

Vi har her set nogle udvidelsesmetoder, der er klar til brug i systemet. Derudover undersøgte vi forskellige muligheder for udvidelsesmetoder. Nogle eksempler på denne funktionalitet kan findes på GitHub.


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