プログラマになる

組み込みエンジニアで主にC言語、時々C#をやっている人の技術メモです。最近はAndroidも少しずつ勉強中

Kotlinで内部クラスを理解する

はじめに

Kotlin in Actionを読み進めているとき内部クラスが全く理解できませんでした
なので今回は内部クラスについて調べたこと実践したことをまとめてみたいと思います。

調べる

クラス内に宣言されたクラスを内部クラスと呼ぶらしいです。
これだけ説明されると普段やるクラス内にクラスを定義するのと何が違うのかという疑問が出ると思います。
以下のコードと何が違うんだろうかと、筆者がクラス内に宣言されたクラスと説明されたとき思いました。

クラスの中にクラスを定義

class ClassA {
  class ClassB() { 
    ...
  }
}

疑問を解決するために調べてみたところ、
内部クラス内部クラスにとっての外部クラスのインスタンス(エンクロージングインスタンス)にアクセスできるらしい。
以下のコードはあくまでもイメージですが、ClassBからClassAにアクセスような感じでしょうか?

class ClassA {
  val id = "ClassAですよ"
  class ClassB() { 
    fun getOuterId() : String = ClassA.id
  }
}

はっきり言って説明されてもよくわからないので実際にコードを書いて確認してみます。

実践する

Kotlinではクラス定義にInner識別子をつけることで内部クラスを定義できます。
なので外部クラスとしてOuterクラス、内部クラスとしてInnerクラスを定義しました。

class Outer {
    inner class Inner {
     
    }
}

次に内部クラスの特徴であるエンクロージングインスタンスへアクセスするための仕組みを実装します。
内部クラスからエンクロージングインスタンスへはthis@クラス名.プロパティ名でアクセスできるようです。
なのでOuterクラスにIDプロパティ、InnerクラスにGetOuterIdにてthis@Outer.idでアクセスします。

class Outer {
    val id = "アウターですよ"
    inner class Inner {
      fun getOuterId() : String = this@Outer.id
    }
}

後はInnerクラスのインスタンスを生成してエンクロージングインスタンスへアクセスするのみです。
Innerクラスのインスタンスを生成するにはエンクロージングインスタンスが必要になるので
エンクロージングエンスタンスを生成したあとにInnerインスタンスを生成してIDプロパティを出力します。

fun main(args: Array<String>) {
    val enclosing_instance = Outer()
    val inner = enclosing_instance.Inner()
    println(inner.getOuterId())
}

Output
------------------------------------------
アウターですよ
Process finished with exit code 0
------------------------------------------

無事、エンクロージングインスタンスにアクセスすることができました。

おわりに

調べたこと実践したことをまとめます。

  • 内部クラスを作るときはClassにinner識別子をつける。
  • 内部クラスを定義すると内部クラスのインスタンスからエンクロージングインスタンスにアクセスすることができる。
  • 内部クラスのインスタンスからthis@クラス名.プロパティ名でエンクロージングクラスにアクセスできる。
  • 内部クラスを生成するときにはエンクロージングクラスが必要になる

参考

以下の書籍・記事を参考にしながら本記事を作成しました。