この記事で分かること
- Kotlinでシングルトンを実装する方法
- object と companion objectの初期化タイミングの違い
- サーバサイドKotlinでの使いみち
この記事では、Kotlinの object 宣言 (object declarations) の書き方や注意点について解説します。
Kotlinでは言語仕様としてデザインパターンのSingleton パターンが用意されており、Javaのように開発者が個別に実装する必要はありません。
使い方も簡単なのですぐに理解できると思います。
サンプルコードをこのページに載せているので実装の参考にして下さい。
また、object宣言 の公式リファレンスは以下のリンクから参照できます。
https://kotlinlang.org/docs/object-declarations.html#object-declarations-overview
object サンプルコード
通常のクラス定義と同様の形式ですが、class キーワードの代わりに object と書いてクラスを定義します。
これだけで難しいスレッド制御を気にすることなく、シングルトンパターンを実現することが出来ます。
メソッドの呼び出し方は、クラス名.メソッド名()
なので Javaの public staticなメソッドと同じ形式になります。
object Service {
private val repository = Repository
private var variable = "some variable" // varも宣言可能
fun findById(id: Long) {
repository.findById(id)
}
}
object Repository {
fun findById(id: Long) {
println("Query DB")
}
}
objectと companion object の違い
シングルトンパターンを実現する方法として、companion object を使う方法も考えられますが、両者には微妙な違いがあります。
具体的には、オブジェクトが初期化されるタイミングが異なっています。(参考:オブジェクト式と宣言 - Kotlin Programming Language)
init ブロックで囲っているコードがある場合、
- object宣言では、初回アクセス時まで初期化が遅延されます
- companion objectでは、クラスがロードされた時点で初期化されます。これはJava の静的初期化子 (staticブロック) と同様です
正直あまり大きな違いではないので、深刻な問題になることは少ないと思いますが、知識として知っているとトラブルシューティング時に役立つかもしれません。
言葉で説明するよりもコードの動きを見た方が理解できると思うので、検証に使ったコードを以下に記載します。
検証コード
object ObjectDeclaration {
init {
println("ObjectDeclaration is initialized.")
}
fun print() {
println("ObjectDeclaration")
}
}
class CompanionObject {
companion object {
init {
println("CompanionObject is initialized.")
}
fun print() {
println("CompanionObject")
}
}
}
//val objectDeclaration = ObjectDeclaration() objectはインスタンス生成できない
val companionObject = CompanionObject() // companion objectが含まれるクラスが生成された時点で初期化が実施される
fun main(args: Array<String>) {
Thread.sleep(3000) // 3秒 wait
ObjectDeclaration.print() // objectはここで初めて初期化される
CompanionObject.print()
}
実行結果
CompanionObject is initialized.
(after 3 seconds...)
ObjectDeclaration is initialized.
ObjectDeclaration
CompanionObject
ObjectDeclaration の方は3秒間の sleep 後、メソッドが呼び出されたタイミングで初期化されていることが分かります。
ちょっと豆知識
object の方は純粋なシングルトンパターンを表現していますが、companion object の方はあるクラス専用のシングルトンインスタンスを定義するような形で実装されています。(companion objectも内部的にはシングルトンのインスタンスで実現されており、Javaの public static classとは異なります)
Java から Kotlinを呼び出す場合、上記のサンプルコードの場合、
ObjectDeclaration.INSTANCE.print();
CompanionObject.Companion.print();
という違いがあります。
サーバサイドでの使いみち
サーバサイドのフレームワークとして最も有名な Spring framework など、フレームワークの機能として DI (依存性の注入) を持っている場合、わざわざ object宣言を使う理由がないと思います。(DIで注入されたインスタンスは基本的にシングルトンです)
しかし、DIの無いフレームワークを利用する場合などには object宣言を使うと便利なケースがあると思います。
まとめ
最後にこの記事の内容をまとめます。
要点
- Kotlinは言語仕様としてシングルトンが用意されている
- object 宣言されたクラスはスレッドセーフにインスタンスを生成することができる
- object と companio object は初期化のタイミングが若干異なる
使い方が簡単なので、比較的初心者にも理解しやすいと感じました。
シングルトンパターンを知っている方なら違和感なく使っていけると思います。
また、objectクラスはサーバサイドで Spring bootを使うと必要のない機能ですが、Ktorなどの軽量フレームワークでは DI の機能がないため、objectクラスで代用するのも一つの選択肢になるのではないかと思います。
最後まで読んで頂きありがとうございました。
参考サイト
- kotlinのcompanionとcompanion objectをちゃんとまとめる。 - Qiita
- KotlinのSingletonについて - Qiita
- Kotlinのcompanion objectとは - Qiita