Kotlin Object キーワード

Object keyword

多くのオブジェクト指向プログラミング言語では、Singletonパターンを実装するために、通常はクラスのコンストラクタをprivateにし、private staticなメンバーにインスタンスを保持し、ファクトリーメソッドでキャッシュ済みの同一インスタンスを返す方法が典型的である。

しかし、Kotlinには明示的なstatic修飾子がない。では、Singletonを実装するにはどうすればよいだろうか。

ここではKotlinのobjectキーワードの使い方と、Singletonの実装について紹介する。

objectには主に3つの利用形態がある。Object式、Object宣言、Companion Objectである。

Object式

Object式は匿名クラスのインスタンスを生成する。既存クラスのプロパティやメソッドをオーバーライドできる。

次の例では、MouseAdapterクラスを再定義し、その匿名インスタンスを作成してaddMouseListener()の引数として渡している。

window.addMouseListener(object : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) { /*...*/ }
    override fun mouseEntered(e: MouseEvent) { /*...*/ }
})

Object宣言(シングルトン)

Kotlinでは、クラスにobjectキーワードを使ってオブジェクトを宣言すると、唯一の1つのインスタンスを持つシングルトンクラスが作られる。

次の例では、Configというシングルトンオブジェクトを作成している。

object Config {
    var maxCount = 100
}

fun main() {
    println(Config.maxCount)
}

Output:

100

シングルトンのメソッドは、静的メソッドのようにクラス名.メソッド名で呼び出せる。

object Foo {
    fun hello() = println("Hello!")
}

fun main() {
    Foo.hello()
}

Output:

Hello!

次の例では、Listデータを保存するクラスを実装してみる。

object FruitStore {
    private val fruits = mutableListOf<String>()

    fun add(s: String) {
        fruits.add(s)
    }

    fun printList() {
        for (item in fruits) {
            println(item)
        }
    }
}

fun main() {
    FruitStore.add("Apple")
    FruitStore.add("Orange")
    FruitStore.add("Banana")
    FruitStore.printList()
}

Output:

Apple
Orange
Banana

このクラスを使う側では、インスタンスを生成せずにメソッドを呼び出していることが分かる。

Companion Object(静的メソッド)

companion objectは、クラス内で静的メソッドのようなメソッドを定義するときに使う。

class Math {
    companion object {
        fun add(a: Int, b: Int) = a + b
    }
}

fun main() {
    println(Math.add(3, 5))
}

Output:

8

次の例では、Listデータを保存するシングルトン風のクラスを実装してみる。

class FruitFactoryStore {
    companion object Factory {
        private val items = mutableListOf<String>()
        fun create(): FruitFactoryStore = FruitFactoryStore()
    }

    fun add(s: String) {
        items.add(s)
    }

    fun printList() {
        for (item in items) {
            println(item)
        }
    }
}

fun main() {
    val store = FruitFactoryStore.create()
    store.add("Apple")
    store.add("Orange")
    store.add("Banana")
    store.printList()
    
    println("-")
    
    val store2 = FruitFactoryStore.create()
    store2.printList()
}

Output:

Apple
Orange
Banana
------
Apple
Orange
Banana

結果を見ると、store2には項目を追加していないにもかかわらず、出力するとデータが表示されることが分かる。

上の例では、companion objectの後に便宜上Factoryという名前を付けているが、これは任意である。呼び出すときもFactoryを指定する必要はなく、そのクラスの直接のメソッドであるかのように呼び出せる。