Kotlin nestede og indre klasser

1. Introduksjon

I denne opplæringen vil vi se på fire måter å lage nestede og indre klasser i Kotlin.

2. Rask sammenligning med Java

For de som tenker på Java-nestede klasser, la oss gjøre en rask oversikt over relaterte termer:

KotlinJava
Indre klasserIkke-statisk nestede klasser
Lokale klasserLokale klasser
Anonyme objekterAnonyme klasser
Nestede klasserStatisk nestede klasser

Selv om det absolutt ikke er identisk, kan vi bruke denne tabellen som en veiledning når vi tenker på mulighetene og bruker tilfeller for hver.

3. Indre klasser

Først kan vi erklære en klasse i en annen klasse ved hjelp av nøkkelordet indre.

Disse klassene har tilgang til medlemmer av den omsluttende klassen, til og med private medlemmer.

For å bruke den, må vi først opprette en forekomst av den ytre klassen; vi kan ikke bruke indre klasser uten det.

La oss lage en Hardisk indre klasse inne i Datamaskin klasse:

class Computer (val model: String) {inner class HardDisk (val sizeInGb: Int) {fun getInfo () = "Installed on $ {[email protected]} with $ sizeInGb GB"}}

Merk at vi bruker et kvalifisert uttrykk for å få tilgang til medlemmer av Datamaskin klasse, som ligner på når vi gjør det Datamaskin. Dette i Java-ekvivalenten til Hardisk.

La oss nå se det i aksjon:

@Test fun givenHardDisk_whenGetInfo_thenGetComputerModelAndDiskSizeInGb () {val hardDisk = Computer ("Desktop"). HardDisk (1000) assertThat (hardDisk.getInfo ()) .isEqualTo ("Installed on Computer (model = Desktop) with 1000 GB")}

4. Lokale indre klasser

Deretter kan vi definere en klasse inne i metodenes kropp eller i en omfangsblokk.

La oss lage et raskt eksempel for å se hvordan det fungerer.

La oss først definere en strøm på metode for vår Datamaskin klasse:

morsom powerOn (): streng {// ...}

Inne i strøm på metode la oss erklære en Ledet klasse og få den til å blinke:

fun powerOn (): String {class Led (val color: String) {fun blink (): String {return "blinking $ color"}} val powerLed = Led ("Green") return powerLed.blink ()}

Merk at omfanget av Ledet klasse er bare inne i metoden.

Med lokale indre klasser kan vi få tilgang til og endre variabler som er erklært i det ytre omfanget. La oss legge til en defaultColor i strøm på metode:

fun powerOn (): String {var defaultColor = "Blue" // ...} 

La oss nå legge til en changeDefaultPowerOnColor i vår Ledet klasse:

klasse Led (valfarge: String) {// ... fun changeDefaultPowerOnColor () {defaultColor = "Violet"}} val powerLed = Led ("Green") log.debug ("defaultColor is $ defaultColor") powerLed.changeDefaultPowerOnColor ( ) log.debug ("defaultColor endret i Led" + "klasse til $ defaultColor")

Hvilke utganger:

[main] DEBUG c.b.n.Computer - defaultColor is Blue [main] DEBUG c.b.n.Computer - defaultColor endret i LED-klasse til Violet

5. Anonyme objekter

Anonyme objekter kan brukes til å definere en implementering av et grensesnitt eller en abstrakt klasse uten å skape en gjenbrukbar implementering.

En stor forskjell mellom anonyme objekter i Kotlin og anonyme indre klasser i Java er at anonyme objekter kan implementere flere grensesnitt og metoder.

La oss først legge til en Bryter grensesnitt i vårt Datamaskin klasse:

interface Switcher {fun on (): String}

La oss nå legge til en implementering av dette grensesnittet i strøm på metode:

fun powerOn (): String {// ... val powerSwitch = object: Switcher {override fun on (): String {return powerLed.blink ()}} return powerSwitch.on ()}

Som vi kan se, for å definere vår anonyme strømbryteren objekt bruker vi et objektuttrykk. Vi må også ta i betraktning at hver gang objektuttrykket påkalles, opprettes en ny forekomst av objektet.

Med anonyme objekter som indre klasser kan vi endre variabler som tidligere er deklarert i omfanget. Dette er fordi Kotlin ikke har den effektive endelige begrensningen vi har forventet i Java.

La oss nå legge til en changeDefaultPowerOnColor i vår Strømbryteren innvende og kall det:

val powerSwitch = object: Switcher {// ... fun changeDefaultPowerOnColor () {defaultColor = "Yellow"}} powerSwitch.changeDefaultPowerOnColor () log.debug ("defaultColor endret i powerSwitch" + "anonymt objekt til $ defaultColor")

Vi ser en utgang som dette:

... [hoved] DEBUG c.b.n.Computer - standardFarge endret inne i powerSwitch anonymt objekt til Yellow

Vær også oppmerksom på at hvis objektet vårt er en forekomst av et grensesnitt eller en klasse med en enkelt abstrakt metode; vi kan lage det ved hjelp av et lambdauttrykk.

6. Nestede klasser

Og sist, vi kan definere en klasse i en annen klasse uten søkeordet indre:

klasse Computer (valmodell: String) {class MotherBoard (val produsent: String)}

I denne typen klasser har vi ikke tilgang til den ytre klasseinstansen. Men vi får tilgang ledsagerobjekt medlemmer av den omsluttende klassen.

Så la oss definere en ledsagerobjekt inne i vår Datamaskin klasse for å se det:

ledsagerobjekt {const val originCountry = "Kina" moro getBuiltDate (): streng {return "2018-07-15T01: 44: 25.38Z"}}

Og så en metode inni Motherboard for å få informasjon om det og den ytre klassen:

moro getInfo () = "Laget av $ produsent - $ originCountry - $ {getBuiltDate ()}"

Nå kan vi teste det for å se hvordan det fungerer:

@Test fun givenMotherboard_whenGetInfo_thenGetInstalledAndBuiltDetails () {val motherBoard = Computer.MotherBoard ("MotherBoard Inc.") assertThat (motherBoard.getInfo ()) .isEqualTo ("Laget av MotherBoard Inc. installert i Kina - 2018-05-23")}

Som vi kan se, skaper vi moderkort uten en forekomst av Datamaskin klasse.

7. Konklusjon

I denne artikkelen har vi sett hvordan vi kan definere og bruke nestede og indre klasser i Kotlin for å gjøre koden vår mer kortfattet og innkapslet.

Vi har også sett noen likheter med tilsvarende Java-konsepter.

Et fullt fungerende eksempel for denne opplæringen finner du på GitHub.