1. 序章

このチュートリアルでは、Kotlinでネストされたクラスと内部クラスを作成する4つの方法を見ていきます。

2. Javaとの簡単な比較

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

Kotlin Java
インナークラス 非静的ネストクラス
ローカルクラス ローカルクラス
匿名オブジェクト 匿名クラス
ネストされたクラス 静的にネストされたクラス

確かに同一ではありませんが、それぞれの機能とユースケースを考える際のガイドとしてこの表を使用できます。

3. インナークラス

まず、キーワードinner を使用して、別のクラス内でクラスを宣言できます。

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

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

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

class Computer(val model: String) {
    inner class HardDisk(val sizeInGb: Int) {
        fun getInfo() = "Installed on ${this@Computer} with $sizeInGb GB"
    }
}

修飾されたこの式を使用してComputerクラスのメンバーにアクセスすることに注意してください。これは、JavaでComputer.thisを実行する場合と同様です。 X184X]HardDisk。

それでは、実際の動作を見てみましょう。

@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. 匿名オブジェクト

匿名オブジェクトを使用すると、再利用可能な実装を作成せずに、インターフェイスまたは抽象クラスの実装を定義できます

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 オブジェクトを定義するには、オブジェクト式を使用します。 また、オブジェクト式が呼び出されるたびに、オブジェクトの新しいインスタンスが作成されることを考慮する必要があります。

内部クラスのような匿名オブジェクトを使用すると、以前にスコープで宣言された変数を変更できます。 これは、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)
}

このタイプのクラスでは、外部クラスインスタンスにアクセスできません。 しかし、私たちはアクセスすることができますコンパニオンオブジェクト囲んでいるクラスのメンバー。

それでは、Computerクラス内にコンパニオンオブジェクトを定義して確認しましょう。

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の概念といくつかの類似点があります。

このチュートリアルの完全に機能する例は、GitHubにあります。