Kotlin verschachtelte und innere Klassen

1. Einleitung

In diesem Tutorial werden vier Möglichkeiten zum Erstellen verschachtelter und innerer Klassen in Kotlin beschrieben.

2. Schneller Vergleich mit Java

Lassen Sie uns für diejenigen, die über verschachtelte Java-Klassen nachdenken, einen kurzen Überblick über verwandte Begriffe geben:

Kotlin Java
Innere Klassen Nicht statische verschachtelte Klassen
Lokale Klassen Lokale Klassen
Anonyme Objekte Anonyme Klassen
Verschachtelte Klassen Statisch verschachtelte Klassen

Obwohl dies sicherlich nicht identisch ist, können wir diese Tabelle als Leitfaden verwenden, wenn wir über die Funktionen und Anwendungsfälle für jeden einzelnen nachdenken.

3. Innere Klassen

Erstens können wir eine Klasse innerhalb einer anderen Klasse mit dem Schlüsselwort inner deklarieren .

Diese Klassen haben Zugriff auf Mitglieder der einschließenden Klasse, auch auf private Mitglieder .

Um es zu verwenden, müssen wir zuerst eine Instanz der äußeren Klasse erstellen. wir können keine inneren Klassen ohne sie benutzen.

Erstellen wir eine innere Festplattenklasse in einer Computerklasse :

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

Beachten Sie, dass wir einen qualifizierten Ausdruck verwenden, um auf Mitglieder der Computer- Klasse zuzugreifen . Dies ähnelt der Ausführung von Computer.this im Java-Äquivalent von HardDisk .

Lassen Sie es uns jetzt in Aktion sehen:

@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 innere Klassen

Als Nächstes können wir eine Klasse im Hauptteil einer Methode oder in einem Bereichsblock definieren .

Lassen Sie uns ein kurzes Beispiel machen, um zu sehen, wie es funktioniert.

Definieren wir zunächst eine powerOn- Methode für unsere Computerklasse :

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

Innerhalb der powerOn- Methode deklarieren wir eine Led- Klasse und lassen sie blinken:

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

Beachten Sie, dass der Bereich der Led- Klasse nur innerhalb der Methode liegt.

Mit lokalen inneren Klassen können wir auf Variablen zugreifen und diese ändern, die im äußeren Bereich deklariert sind . Fügen wir der powerOn- Methode eine defaultColor hinzu :

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

Fügen wir nun eine changeDefaultPowerOnColor in unsere Led- Klasse ein:

class Led(val color: String) { //... fun changeDefaultPowerOnColor() { defaultColor = "Violet" } } val powerLed = Led("Green") log.debug("defaultColor is $defaultColor") powerLed.changeDefaultPowerOnColor() log.debug("defaultColor changed inside Led " + "class to $defaultColor")

Welche Ausgänge:

[main] DEBUG c.b.n.Computer - defaultColor is Blue [main] DEBUG c.b.n.Computer - defaultColor changed inside Led class to Violet

5. Anonyme Objekte

Anonyme Objekte können verwendet werden, um eine Implementierung einer Schnittstelle oder einer abstrakten Klasse zu definieren, ohne eine wiederverwendbare Implementierung zu erstellen .

Ein großer Unterschied zwischen anonymen Objekten in Kotlin und anonymen inneren Klassen in Java besteht darin, dass anonyme Objekte mehrere Schnittstellen und Methoden implementieren können.

Fügen wir zunächst eine Switcher- Schnittstelle in unsere Computerklasse ein:

interface Switcher { fun on(): String }

Fügen wir nun eine Implementierung dieser Schnittstelle in die powerOn- Methode ein:

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

Wie wir sehen können, verwenden wir zum Definieren unseres anonymen powerSwitch- Objekts einen Objektausdruck. Außerdem müssen wir berücksichtigen, dass jedes Mal, wenn der Objektausdruck aufgerufen wird, eine neue Instanz des Objekts erstellt wird.

Mit anonymen Objekten wie inneren Klassen können wir Variablen ändern, die zuvor im Bereich deklariert wurden. Dies liegt daran, dass Kotlin nicht die endgültige Einschränkung hat, die wir in Java erwartet haben.

Nun lassen Sie uns einen hinzufügen changeDefaultPowerOnColor in unserem Power Objekt und nennen es:

val powerSwitch = object : Switcher { //... fun changeDefaultPowerOnColor() { defaultColor = "Yellow" } } powerSwitch.changeDefaultPowerOnColor() log.debug("defaultColor changed inside powerSwitch " + "anonymous object to $defaultColor")

Wir werden eine Ausgabe wie diese sehen:

... [main] DEBUG c.b.n.Computer - defaultColor changed inside powerSwitch anonymous object to Yellow

Beachten Sie außerdem, dass unser Objekt eine Instanz einer Schnittstelle oder einer Klasse mit einer einzelnen abstrakten Methode ist. Wir können es mit einem Lambda-Ausdruck erstellen.

6. Verschachtelte Klassen

Und zuletzt können wir eine Klasse innerhalb einer anderen Klasse ohne das Schlüsselwort inner definieren :

class Computer(val model: String) { class MotherBoard(val manufacturer: String) }

In this type of class, we don't have access to the outer class instance. But, we can access companion object members of the enclosing class.

So, let's define a companion object inside our Computer class to see it:

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

And then a method inside MotherBoard to get information about it and the outer class:

fun getInfo() = "Made by $manufacturer - $originCountry - ${getBuiltDate()}"

Now, we can test it to see how it works:

@Test fun givenMotherboard_whenGetInfo_thenGetInstalledAndBuiltDetails() { val motherBoard = Computer.MotherBoard("MotherBoard Inc.") assertThat(motherBoard.getInfo()) .isEqualTo( "Made by MotherBoard Inc. installed in China - 2018-05-23") }

As we can see, we create motherBoard without an instance of Computer class.

7. Conclusion

In this article, we’ve seen how to define and use nested and inner classes in Kotlin to make our code more concise and encapsulated.

Also, we've seen some similarities to the corresponding Java concepts.

Ein voll funktionsfähiges Beispiel für dieses Tutorial finden Sie auf GitHub.