Kotlinの入れ子クラスと内部クラス

1前書き

このチュートリアルでは、https://www.baeldung.com/kotlin-overview[Kotlin]でネストしたクラスと内部クラスを作成する4つの方法を見ていきます。

2 Java との簡単な比較

Javaの入れ子になったクラス について考えている人のために、関連する用語を簡単にまとめてみましょう。

| =========================== | Kotlin | Java |内部クラス| Non |静的ネストクラス|ローカルクラス|ローカルクラス|匿名オブジェクト|匿名クラス|ネストクラス|静的ネストクラス| | | ===========================================

まったく同じではありませんが、それぞれの機能とユースケースについて考えるときに、この表をガイドとして使用できます。

3内部クラス

  • 最初に、キーワード inner ** を使って別のクラスの中でクラスを宣言することができます。

これらのクラスは、囲んでいるクラスのメンバー、プライベートのメンバーにもアクセスできます。

それを使用するには、最初に外部クラスのインスタンスを作成する必要があります。それなしでは内部クラスを使うことはできません。

Computer クラスの中に HardDisk 内部クラスを作成しましょう。

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

Computer クラスのメンバーにアクセスするには、https://kotlinlang.org/docs/reference/this-expressions.html[この式を修飾]を使用します。これは、同等のJavaで Computer.this を実行する場合と同様です。 ハードディスク

それでは、実際に見てみましょう。

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

4ローカルインナークラス

  • 次に、クラスをメソッドの本体内またはスコープブロック内で定義できます。

それがどのように機能するかを見るために簡単な例を作りましょう。

まず、 Computer クラスに powerOn メソッドを定義しましょう。

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

powerOn メソッド内で Led クラスを宣言して点滅させましょう。

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

Led クラスの有効範囲はメソッド内にのみあることに注意してください。

  • ローカル内部クラスを使えば、外部スコープで宣言された変数にアクセスして変更することができます。 powerOn メソッドに defaultColor を追加しましょう。

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

それでは、 Led クラスに changeDefaultPowerOnColor を追加しましょう。

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

どの出力:

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

5匿名オブジェクト

  • Anonymous objects を使用すると、再利用可能な実装を作成することなく、インタフェースまたは抽象クラスの実装を定義できます。

Kotlinの匿名オブジェクトとJavaの匿名内部クラスの大きな違いは、匿名オブジェクトは複数のインタフェースとメソッドを実装できるということです。

まず、 Computer クラスに Switcher インターフェースを追加しましょう。

interface Switcher {
    fun on(): String
}

それでは、 powerOn メソッド内にこのインターフェースの実装を追加しましょう。

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

ご覧のとおり、匿名の powerSwitch オブジェクトを定義するには、https://kotlinlang.org/docs/reference/object-declarations.html#object-expressions[object expression]を使用します。また、オブジェクト式が呼び出されるたびに、オブジェクトの新しいインスタンスが作成されることを考慮する必要があります。

内部クラスのような無名オブジェクトを使うと、以前スコープ内で宣言された変数を修正することができます。これは、Kotlinには、Javaで期待されるようになる実質的に最終的な制限がないためです。

それでは、 __ PowerSwitch オブジェクトに changeDefaultPowerOnColor__を追加し、それを呼び出します。

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

次のような出力が表示されます。

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

また、オブジェクトがインタフェースのインスタンスまたは単一の抽象メソッドを持つクラスの場合は、この点に注意してください。 [λ式 を使用して作成できます。

6. 入れ子クラス

そして最後に、** キーワードinnerを使わずに、クラスを別のクラスの中に定義することができます。

class Computer(val model: String) {
    class MotherBoard(val manufacturer: String)
}
  • このタイプのクラスでは、外側のクラスインスタンスにアクセスすることはできません** しかし、外側のクラスのhttps://www.baeldung.com/kotlin-objects[ companion object ]メンバーにアクセスすることはできます。

それで、それを見るために Computer クラスの中に companion object を定義しましょう:

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

それから MotherBoard 内のメソッドとそれに関する情報を取得するためのメソッド:

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

これで、それがどのように機能するのかをテストすることができます。

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

ご覧のとおり、 Computer クラスのインスタンスなしで motherBoard を作成しています。

7. 結論

この記事では、コードをより簡潔にしてカプセル化するために、Kotlinで入れ子クラスと内部クラスを定義して使用する方法を説明しました。

また、対応するJavaの概念との類似点もいくつかあります。

このチュートリアルの完全に有効な例はhttps://github.com/eugenp/tutorials/tree/master/core-kotlin[GitHubに追加]を見つけることができます。