Classes imbriquées et internes à Kotlin

Kotlin imbriqué et classes intérieures

1. introduction

Dans ce didacticiel, nous allons examiner quatre façons de créer des classes imbriquées et internes dansKotlin.

2. Comparaison rapide avec Java

Pour ceux qui pensent àJava nested classes, faisons un bref aperçu des termes associés:

Kotlin

Java

Classes intérieures

Classes imbriquées non statiques

Cours locaux

Cours locaux

Objets anonymes

Classes Anonymes

Classes imbriquées

Classes imbriquées statiques

Bien que ce ne soit certainement pas identique, nous pouvons utiliser ce tableau comme guide pour réfléchir aux capacités et aux cas d'utilisation de chacun.

3. Classes intérieures

First, we can declare a class inside another class using the keyword inner.

Ces classeshave access to members of the enclosing class, even private members.

Pour l'utiliser, nous devons d'abord créer une instance de la classe externe; nous ne pouvons pas utiliser de classes internes sans cela.

Créons une classe interneHardDisk dans une classeComputer:

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

Notez que nous utilisons unqualified this expression pour accéder aux membres de la classeComputer, ce qui est similaire à lorsque nous faisonsComputer.this dans l'équivalent Java deHardDisk.

Voyons maintenant cela en action:

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

4. Classes intérieures locales

Next, we can define a class inside a method’s body or in a scope block.

Prenons un exemple rapide pour voir comment cela fonctionne.

Tout d'abord, définissons une méthodepowerOn pour notre classeComputer:

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

À l'intérieur de la méthodepowerOn déclarons une classeLed et faisons-la clignoter:

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

Notez que la portée de la classeLed est uniquement à l'intérieur de la méthode.

With local inner classes, we can access and modify variables declared in the outer scope. Ajoutons undefaultColor dans la méthodepowerOn:

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

Maintenant, ajoutons unchangeDefaultPowerOnColor dans notre classeLed:

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")

Quelles sorties:

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

5. Objets anonymes

Anonymous objects can be used to define an implementation of an interface or an abstract class without creating a reusable implementation.

Une grande différence entre les objets anonymes dans Kotlin et les classes internes anonymes en Java est queanonymous objects can implement multiple interfaces and methods.

Tout d'abord, ajoutons une interfaceSwitcher dans notre classeComputer:

interface Switcher {
    fun on(): String
}

Maintenant, ajoutons une implémentation de cette interface dans la méthodepowerOn:

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

Comme nous pouvons le voir, pour définir notre objetpowerSwitch anonyme, nous utilisons unobject expression. De plus, nous devons tenir compte du fait que chaque fois que l'expression d'objet est invoquée, une nouvelle instance de l'objet est créée.

Avec des objets anonymes comme les classes internes, nous pouvons modifier des variables précédemment déclarées dans la portée. En effet, Kotlin n’a pas la restriction finale à laquelle nous nous attendions en Java.

Maintenant, ajoutons unchangeDefaultPowerOnColor dans notre sobjectPowerSwitch et appelons-le:

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

Nous verrons un résultat comme celui-ci:

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

Notez également que si notre objet est une instance d'une interface ou une classe avec une seule méthode abstraite; nous pouvons le créer en utilisant unlambda expression.

6. Classes imbriquées

Et enfin,we can define a class inside another class without the keyword inner:

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. Mais, nous pouvons accéder aux membrescompanion object de la classe englobante.

Alors, définissons uncompanion object dans notre classeComputer pour le voir:

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

Et puis une méthode à l'intérieur deMotherBoard pour obtenir des informations à ce sujet et sur la classe externe:

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

Maintenant, nous pouvons le tester pour voir comment cela fonctionne:

@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")
}

Comme nous pouvons le voir, nous créonsmotherBoard sans instance de la classeComputer.

7. Conclusion

Dans cet article, nous avons vu comment définir et utiliser des classes imbriquées et internes dans Kotlin pour rendre notre code plus concis et encapsulé.

Nous avons également constaté des similitudes avec les concepts Java correspondants.

Un exemple entièrement fonctionnel pour ce didacticiel peut être trouvéover on GitHub.