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 として作成されています。

content-img

クラス内で 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 におけるシングルトンの実装方法を紹介しました。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Kotlin 入門