Singleton の実装
シングルトンパターンを実装する場合、多くのオブジェクト指向プログラミング言語では、 コンストラクタを private にして、クラスの private で static なメンバーでインスタンスを保持し、 ファクトリメソッドで、キャッシュしてある同じインスタンスを返す、とするのが典型的な実装方法です。
ところが Kotlin では明示的に「スタティック」という修飾子がありません。シングルトンを実装するにはどうしたらよいでしょうか。
ここでは Kotlin でのシングルトンの実装例を紹介します。
object で実装する
Kotlin で匿名のデータオブジェクトを作るときに、object というキーワードが使えるのですが、 これに名前を付けて使うと、static final 的な動作が実現できます。
例えば、次のようにリストを保持する ItemStore クラスを実装します。
package test object ItemStore { private val items = mutableListOf<String>() fun add(s: String) { items.add(s) } fun printItems(tag: String = "") { println("[$tag] ===============") for (item in items) { println(item) } } }
これを使う側では、次のようにインスタンスを生成することなく、メソッドが呼び出せます。
import test.ItemStore fun main() { ItemStore.add("Apple") ItemStore.add("Orange") ItemStore.add("Banana") ItemStore.printItems("1") }
実行結果は次の通りです。
[1] =============== Apple Orange Banana
念の為、デコンパイルして Java コードを確認すると、確かに内部で保持しているリストは ItemStore クラスの private static final として作成されています。
クラス内で object を宣言する
class 内で object を宣言する場合は、 companion キーワードでマークします。
package test class ItemStore { companion object Factory { private val items = mutableListOf<String>() fun create(): ItemStore = ItemStore() } fun add(s: String) { items.add(s) } fun printItems(tag: String = "") { println("[$tag] ===============") for (item in items) { println(item) } } }
ここでは companion object に Factory という名前を便宜上付けていますが、省略可能です。 利用時にも指定する必要がなく、そのクラスの直接のメソッドであるかのようにメソッドを呼び出せます。
呼び出し例と実行結果は次の通りです。
import test.ItemStore fun main() { val store = ItemStore.create() store.add("Apple") store.add("Orange") store.add("Banana") store.printItems("1") val store2 = ItemStore.create() store2.printItems("2") }
[1] =============== Apple Orange Banana [2] =============== Apple Orange Banana
確かに store2 はアイテムを追加していないのに、 プリントすると確かにデータが表示されています。
少々長くなりますが、以下にデコンパイルされた Java コードを示します。(メタデータ等、一部省略しています。private、static、final などの修飾子の確認はできます)
package test; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import kotlin.Metadata; import kotlin.jvm.internal.DefaultConstructorMarker; import kotlin.jvm.internal.Intrinsics; import org.jetbrains.annotations.NotNull; public final class ItemStore { private static final List items; public static final ItemStore.Factory Factory = new ItemStore.Factory((DefaultConstructorMarker)null); public final void add(@NotNull String s) { Intrinsics.checkParameterIsNotNull(s, "s"); items.add(s); } public final void printTodo(@NotNull String tag) { Intrinsics.checkParameterIsNotNull(tag, "tag"); String item = '[' + tag + "] ==============="; boolean var3 = false; System.out.println(item); Iterator var5 = items.iterator(); while(var5.hasNext()) { item = (String)var5.next(); boolean var4 = false; System.out.println(item); } } // $FF: synthetic method public static void printTodo$default(ItemStore var0, String var1, int var2, Object var3) { if ((var2 & 1) != 0) { var1 = ""; } var0.printTodo(var1); } static { boolean var0 = false; items = (List)(new ArrayList()); } public static final class Factory { @NotNull public final ItemStore create() { return new ItemStore(); } private Factory() { } // $FF: synthetic method public Factory(DefaultConstructorMarker $constructor_marker) { this(); } } }
以上で、Kotlin におけるシングルトンの実装方法を紹介しました。