Delegerte eiendommer i Kotlin

1. Introduksjon

Kotlin-programmeringsspråket har naturlig støtte for klasseegenskaper.

Eiendommer støttes vanligvis direkte av tilsvarende felt, men det trenger ikke alltid å være slik - så lenge de er riktig utsatt for omverdenen, kan de fortsatt betraktes som egenskaper.

Dette kan oppnås ved å håndtere dette i getters og settere, eller ved å utnytte kraften til Delegater.

2. Hva er delegerte eiendommer?

Enkelt sagt, delegerte eiendommer støttes ikke av et klassefelt og delegerer å få og sette til et annet stykke kode. Dette gjør at delegert funksjonalitet kan trekkes ut og deles mellom flere lignende egenskaper - f.eks. lagring av eiendomsverdier på et kart i stedet for separate felt.

Delegerte eiendommer brukes ved å erklære eiendommen og delegaten den bruker. De av nøkkelord indikerer at eiendommen kontrolleres av den angitte delegaten i stedet for sitt eget felt.

For eksempel:

class DelegateExample (map: MutableMap) {var name: Streng etter kart}

Dette bruker det faktum at en MutableMap er selv en delegat, slik at du kan behandle nøklene som egenskaper.

3. Standard delegerte egenskaper

Kotlin standardbibliotek leveres med et sett med standard delegater som er klare til bruk.

Vi har allerede sett et eksempel på bruk av en MutableMap for å sikkerhetskopiere en foranderlig eiendom. På samme måte kan du sikkerhetskopiere en uforanderlig eiendom ved hjelp av en Kart - slik at man kan få tilgang til enkelte felt som egenskaper, men aldri endre dem.

De lat delegat tillater at verdien til en eiendom bare beregnes ved førstegangs tilgang og deretter bufres. Dette kan være nyttig for egenskaper som kan være dyre å beregne og som du kanskje ikke trenger - for eksempel å lastes fra en database:

class DatabaseBackedUser (userId: String) {val name: String by lazy {queryForValue ("SELECT name FROM users WHERE userId =: userId", mapOf ("userId" to userId)}}

De observerbar delegat tillater at en lambda kan utløses når verdien av eiendommen endres, for eksempel muliggjør endringsvarsler eller oppdatering av andre relaterte egenskaper:

class ObservedProperty {var name: String by Delegates.observable ("") {prop, old, new -> println ("Old value: $ old, New value: $ new")}}

Fra og med Kotlin 1.4 er det også mulig å delegere direkte til en annen eiendom. Hvis vi for eksempel gir nytt navn til en eiendom i en API-klasse, kan vi la den gamle være på plass og bare delegere til den nye:

klasse RenamedProperty {var newName: String = "" @Deprecated ("Bruk nytt navn i stedet") var navn: Streng etter dette :: newName}

Her, når som helst vi får tilgang til Navn eiendom, bruker vi effektivt nytt navn eiendom i stedet.

4. Opprette dine delegater

Det vil være tider at du vil skrive delegatene dine, i stedet for å bruke de som allerede eksisterer. Dette er avhengig av å skrive en klasse som utvider ett av to grensesnitt - LesOnlyProperty eller ReadWriteProperty.

Begge disse grensesnittene definerer en metode som kalles getValue - som brukes til å levere den nåværende verdien av den delegerte eiendommen når den leses. Dette tar to argumenter og returnerer verdien av eiendommen:

  • thisRef - en referanse til klassen som eiendommen er i
  • eiendom - en refleksjonsbeskrivelse av eiendommen som delegeres

De ReadWriteProperty grensesnitt definerer i tillegg en metode som kalles setValue som brukes til å oppdatere gjeldende verdi av eiendommen når den skrives. Dette tar tre argumenter og har ingen returverdi:

  • thisRef - En referanse til klassen som eiendommen er i
  • eiendom - En refleksjonsbeskrivelse av eiendommen som delegeres
  • verdi - Den nye verdien av eiendommen

Fra og med Kotlin 1.4 ble den ReadWriteProperty grensesnittet faktisk utvides LesOnlyProperty. Dette lar oss skrive en enkelt delegatklasse som implementerer ReadWriteProperty og bruk den til skrivebeskyttede felt i koden vår. Tidligere måtte vi skrive to forskjellige delegater - en for skrivebeskyttede felt og en annen for foranderlige felt.

Som et eksempel, la oss skrive en delegat som alltid fungerer angående en databasetilkobling i stedet for lokale felt:

klasse DatabaseDelegate (readQuery: String, writeQuery: String, id: Any): ReadWriteDelegate {fun getValue (thisRef: R, property: KProperty): T {return queryForValue (readQuery, mapOf ("id" to id))} fun setValue ( thisRef: R, property: KProperty, value: T) {update (writeQuery, mapOf ("id" to id, "value" to value))}}

Dette avhenger av to toppnivåfunksjoner for å få tilgang til databasen:

  • queryForValue - dette tar litt SQL og noen binder og returnerer den første verdien
  • Oppdater - dette tar litt SQL og noen binder og behandler det som en UPDATE-setning

Vi kan da bruke dette som alle vanlige delegater og få klassen vår automatisk støttet av databasen:

klasse DatabaseUser (userId: String) {var name: String by DatabaseDelegate ("SELECT name FROM users WHERE userId =: id", "UPDATE users SET name =: value WHERE userId =: id", userId) var email: String by DatabaseDelegate ("VELG e-post fra brukere WHERE userId =: id", "UPDATE brukere SET E-post =: verdi WHERE userId =: id", userId)}

5. Delegere delegering

En annen ny funksjon som vi har i Kotlin 1.4 er muligheten til å delegere etableringen av våre delegatklasser til en annen klasse. Dette fungerer ved å implementere PropertyDelegateProvider grensesnitt, som har en enkelt metode for å sette i gang noe å bruke som selve delegaten.

Vi kan bruke dette til å utføre litt kode rundt opprettelsen av delegaten som skal brukes - for eksempel for å logge det som skjer. Vi kan også bruke den til å velge delegaten vi skal bruke dynamisk basert på eiendommen den brukes til. Vi kan for eksempel ha en annen delegat hvis eiendommen er ugyldig:

klasse DatabaseDelegateProvider(readQuery: String, writeQuery: String, id: Any): PropertyDelegateProvider {override operator fun provideDelegate (thisRef: T, prop: KProperty): ReadWriteDelegate {if (prop.returnType.isMarkedNullable) {return NullableDatabaseDelegate (readQuery, writeQuery, id)} else {return NonNullDatabaseDelegate (readQuery, writeQuery)

Dette lar oss skrive enklere kode i hver delegat fordi de bare trenger å fokusere på mer målrettede saker. I det ovenstående vet vi det NonNullDatabaseDelegate vil bare noen gang brukes på egenskaper som ikke kan ha en null verdi, så vi trenger ingen ekstra logikk for å håndtere det.

6. Sammendrag

Eiendomsdelegering er en kraftig teknikk som lar deg skrive kode som tar over kontrollen over andre egenskaper, og som hjelper denne logikken til å deles mellom forskjellige klasser. Dette muliggjør robust, gjenbrukbar logikk som ser ut og føles som vanlig tilgang til eiendom.

Et fullt fungerende eksempel for denne artikkelen finner du på GitHub.


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