はじめに
Kotlinのクラスの初期化についてまとめます。参考にするのは以下の書籍。
- 作者: Dmitry Jemerov,Svetlana Isakova,長澤太郎,藤原聖,山本純平,yy_yank
- 出版社/メーカー: マイナビ出版
- 発売日: 2017/10/31
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (2件) を見る
チートシートも見ながら進めます。
Kotlinチートシート発見
— bookstore (@bookstoreTech) 2018年11月4日
Kotlin Cheat Sheet by @marcinmoskala https://t.co/VMRTuvIIeP
Kotlinにおける初期化の種類
ここで言う初期化とは、クラスがインスタンス化される時に実行される処理のことです。Javaで言えばコンストラクタがそれに当たります。Kotlinでは以下の種類の初期化処理を実装できます。
プライマリコンストラクタ
Javaではコンストラクタをいくつも持つことが出来ます。これはKotlinでも同じですが、Kotlinではコンストラクタをプライマリコンストラクタとセカンダリコンストラクタに分けています。
class User(val shortName: String)
このかっこ( ... )
がプライマリコンストラクタです。プライマリコンストラクタを使うことで、プロパティの宣言と初期化を同時に行うことが出来ます。この処理と同等のものは以下のようになります。
class User constructor(_shortName: String) { val shortName: String = _shortName }
ここで注意して欲しいのは、プライマリコンストラクタがプロパティの宣言と初期化を行うのは、引数にval
もしくはvar
が付いている場合のみです。それ以外の場合は単なるコンストラクタの引数として処理されるため、クラスのプロパティとしては宣言されません。
プライマリコンストラクではデフォルト引数を使用することもできます。例えば以下のようにactivate
引数の指定がない場合にはデフォルトでfalse
にするといったこともできます。
class User(val shortName: String, val activate: Boolean = false)
クラスがスーパークラスを持つ場合、スーパークラスのコンストラクタを明示的に呼び出す必要があります。以下の例ではUser
クラスをEmployee
クラスが継承しています。Employeeプライマリコンストラクタの後に続けて書かれているのが、User
クラスのプライマリコンストラクタ呼び出しです。
open class User(val shortName: String, val activate: Boolean = false) class Employee(shortName: String) : User(shortName)
さて、ここまでプライマリコンストラクタの基本的な使い方を書いてきましたが、初期化を行いたい時に特定の処理(イベント通知、バリデーション)などを行いたい場合どうすればいいのでしょうか?そこで登場するのが初期化ブロックです。
初期化ブロック
初期化ブロックを使うことで、クラスを初期化する時に特定の処理を実行できます。
fun main(args: Array<String>) { User("bkst") } object Logger { fun log(msg: String) = println(msg) } class User constructor(_shortName: String) { // 初期化ブロック init { Logger.log("User created") } val shortName: String = _shortName }
初期化ブロックはクラスの中にいくつ書いても大丈夫です。ただ、実行順番は上から順番に行われるみたいです(我調べ)。
セカンダリコンストラクタ
KotlinではJavaと違ってコンストラクタを複数持つ状況がありません。なぜなら、上記のプライマリコンストラクタとデフォルト引数を用いれば引数を複数パターン持つ場合をカバーできるからです。
// Javaの場合 public class User { private String name; private String postalCode; public User(String name) { this(nam, "UnKnown") } public User(String name, String postalCode) { this.name = name; this.postalCode = postalCode; } }
// Kotlinの場合 public class User(val name: String, val postalCode: String = "Unknown")
しかし、複数のコンストラクタを必要とする場合もあります。それは、コンストラクタ引数の種類が違ったり、引数の数に応じてコンストラクタごとの処理を行いたい等といった場合です。そういったときにはKotlinのセカンダリコンストラクを複数宣言することで解決します。
class User { constructor(name: String, postalCode: String) { ... } constructor(userId: UserId) { ... } }
セカンダリコンストラクタはconstructor
キーワードでメソッドのように定義します。
セカンダリコンストラクタから、他のセカンダリコンストラクタを呼び出すことも可能です。
class User { private val postalCode: String private val name: String constructor(name: String, postalCode: String) { this.name = name this.postalCode = postalCode } // 他のコンストラクタ呼び出し constructor(userId: String) : this("Unknown", "Unknown") { ... } }
おわりに
Kotlinは学ぶたびに奥が深いと実感できる言語です。特に、既存言語(特にJava)で手が届かないようなものが綺麗になっていて気持ちいい。