Kotlin geneste en innerlijke klassen

1. Inleiding

In deze tutorial bekijken we vier manieren om geneste en innerlijke klassen in Kotlin te maken.

2. Snelle vergelijking met Java

Voor degenen die nadenken over geneste Java-klassen, laten we een kort overzicht maken van gerelateerde termen:

KotlinJava
Innerlijke klassenNiet-statische geneste klassen
Lokale lessenLokale lessen
Anonieme objectenAnonieme lessen
Geneste klassenStatische geneste klassen

Hoewel zeker niet identiek, kunnen we deze tabel als richtlijn gebruiken bij het nadenken over de mogelijkheden en use cases voor elk.

3. Innerlijke klassen

Ten eerste kunnen we een klasse binnen een andere klasse declareren met behulp van het trefwoord innerlijk.

Deze klassen toegang hebben tot leden van de omringende klas, zelfs tot privé-leden.

Om het te gebruiken, moeten we eerst een instantie van de buitenste klasse maken; we kunnen geen innerlijke klassen gebruiken zonder.

Laten we een Harde schijf innerlijke klasse binnen een Computer klasse:

class Computer (val model: String) {inner class HardDisk (val sizeInGb: Int) {fun getInfo () = "Geïnstalleerd op $ {[email protected]} met $ sizeInGb GB"}}

Merk op dat we een gekwalificeerde deze uitdrukking gebruiken om toegang te krijgen tot leden van de Computer class, wat vergelijkbaar is met wanneer we dat doen Computer. Dit in het Java-equivalent van Harde schijf.

Laten we het nu in actie zien:

@Test plezier gegevenHardDisk_whenGetInfo_thenGetComputerModelAndDiskSizeInGb () {val hardDisk = Computer ("Desktop"). HardDisk (1000) assertThat (hardDisk.getInfo ()) .isEqualTo ("Geïnstalleerd op computer (model = Desktop) met 1000 GB")}

4. Lokale innerlijke klassen

Vervolgens kunnen we een klasse definiëren in de body van een methode of in een scope-blok.

Laten we een snel voorbeeld maken om te zien hoe het werkt.

Laten we eerst een powerOn methode voor onze Computer klasse:

fun powerOn (): String {// ...}

Binnenkant van de powerOn methode laten we een LED klas en laat het knipperen:

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

Merk op dat de reikwijdte van de LED class is alleen binnen de methode.

Met lokale innerlijke klassen hebben we toegang tot variabelen die in de buitenste scope zijn gedeclareerd en kunnen we deze wijzigen. Laten we een defaultColor in de powerOn methode:

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

Laten we nu een changeDefaultPowerOnColor in onze LED klasse:

class Led (val color: String) {// ... fun changeDefaultPowerOnColor () {defaultColor = "Violet"}} val powerLed = Led ("Groen") log.debug ("defaultColor is $ defaultColor") powerLed.changeDefaultPowerOnColor ( ) log.debug ("defaultColor veranderd in Led" + "class naar $ defaultColor")

Welke outputs:

[main] DEBUG c.b.n.Computer - defaultColor is Blue [main] DEBUG c.b.n.Computer - defaultColor binnen Led class gewijzigd in Violet

5. Anonieme objecten

Anonieme objecten kunnen worden gebruikt om een ‚Äč‚Äčimplementatie van een interface of een abstracte klasse te definiëren zonder een herbruikbare implementatie te creëren.

Een groot verschil tussen anonieme objecten in Kotlin en anonieme innerlijke klassen in Java is dat anonieme objecten kunnen meerdere interfaces en methoden implementeren.

Laten we eerst een Switcher interface in onze Computer klasse:

interface Switcher {fun on (): String}

Laten we nu een implementatie van deze interface toevoegen in het powerOn methode:

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

Zoals we kunnen zien, om ons anoniem te definiëren powerSwitch object gebruiken we een objectuitdrukking. We moeten er ook rekening mee houden dat elke keer dat de objectexpressie wordt aangeroepen, een nieuwe instantie van het object wordt gemaakt.

Met anonieme objecten, zoals innerlijke klassen, kunnen we variabelen wijzigen die eerder in het bereik zijn gedeclareerd. Dit komt omdat Kotlin niet de feitelijke laatste beperking heeft die we in Java gewend zijn.

Laten we nu een changeDefaultPowerOnColor in onze PowerSwitch object en noem het:

val powerSwitch = object: Switcher {// ... fun changeDefaultPowerOnColor () {defaultColor = "Geel"}} powerSwitch.changeDefaultPowerOnColor () log.debug ("defaultColor veranderd in powerSwitch" + "anoniem object naar $ defaultColor")

We zullen een output als deze zien:

... [main] DEBUG c.b.n.Computer - defaultColor veranderd in powerSwitch anoniem object naar Geel

Merk ook op dat als ons object een instantie is van een interface of een klasse met een enkele abstracte methode; we kunnen het maken met een lambda-uitdrukking.

6. Geneste klassen

En als laatste, we kunnen een klasse binnen een andere klasse definiëren zonder het trefwoord inner:

class Computer (val model: String) {class Moederbord (val fabrikant: String)}

In dit type klasse hebben we geen toegang tot de instantie van de buitenste klasse. Maar we hebben toegang begeleidend object leden van de omsluitende klas.

Dus laten we een begeleidend object binnen onze Computer klas om het te zien:

begeleidend object {const val originCountry = "China" fun getBuiltDate (): String {return "2018-07-15T01: 44: 25.38Z"}}

En dan een methode binnenin Moederbord om er informatie over en de buitenste klasse te krijgen:

fun getInfo () = "Gemaakt door $ fabrikant - $ originCountry - $ {getBuiltDate ()}"

Nu kunnen we het testen om te zien hoe het werkt:

@Test plezier gegevenMotherboard_whenGetInfo_thenGetInstalledAndBuiltDetails () {val motherBoard = Computer.MotherBoard ("MotherBoard Inc.") assertThat (motherBoard.getInfo ()) .isEqualTo ("Gemaakt door MotherBoard Inc. geïnstalleerd in China - 2018-05-23")}

Zoals we kunnen zien, creëren we moederbord zonder een instantie van Computer klasse.

7. Conclusie

In dit artikel hebben we gezien hoe we geneste en innerlijke klassen in Kotlin kunnen definiëren en gebruiken om onze code beknopter en ingekapseld te maken.

We hebben ook enkele overeenkomsten gezien met de overeenkomstige Java-concepten.

Een volledig werkend voorbeeld voor deze tutorial is te vinden op GitHub.