覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Kotlin - コンパニオンオブジェクト

Javaと異なり Kotlin のクラスは静的なメンバを持つことができません。Javaにおける static キーワードは Kotlin には含まれていません。

この Java における static の代替手段として以下を利用します。

  • トップレベル関数関数
    • 多くの状況で Java の staticメソッドを置き換えることが可能
  • オブジェクト宣言
    • Javaの静的フィールドと静的メソッドの置き換えをします

多くの状況では トップレベル関数 を使うことが推奨されているようです。
しかし、トップレベル関数は、クラスのprivateメンバにアクセスできないという制約が存在します。
クラスのインスタンスなしで、クラス内部にアクセスする必要がある関数を記述する必要がある場合、
対象クラスの内部のオブジェクト宣言のメンバとして関数を書くことが可能です。


クラス内で定義されているオブジェクトの1つに companion というキーワードを付けることが可能です。
このキーワードを付与することで、オブジェクトの名前を明示せずに、クラス名を使って直接そのオブジェクトのメソッド名やプロパティにアクセス可能となります。
Javaにおける静的メソッド呼び出しと全く同じ状態となります。

class Printer {
    companion object {
        fun print(data : String) {
            println("Printer printed... [$data]")
        }
    }
}

呼び出し側のコードは以下のようになります

Printer.print("Kotlin")  // -> Printer printed... [Kotlin]


companion のキーワードを付ける際にどこまで意味があるのか不明ですが、オブジェクト宣言で名前を指定することも可能です

class Printer {
    companion object Java {
        fun print(data : String) {
            println("Printer printed... [$data]")
        }
    }
}

呼び出し側のコードでは companionを付与したオブジェクト宣言の名前を経由してもしなくても内部の関数にアクセス可能です

Printer.print("Kotlin")  // -> Printer printed... [Kotlin]
Printer.Java.print("Java Lang")  // -> Printer printed... [Java Lang]


このコンパニオンオブジェクトの利用の好例が ファクトリメソッド(Javaでいうところのstaticファクトリ) になると思われます。

Javaでも同じですが複数のコンストラクタを持つクラスの場合、コンストラクタには名前を付けることができないためコード上の意味づけがしにくくなります。
そういった場合にはファクトリメソッド(staticファクトリ)を用意して、意味のある名前を付けます。


Kotlin で以下のような2つのセカンダリコンストラクタを持つクラスが存在したとします。

class User {
    val userName : String
    
    constructor(handleName: String) {
        this.userName = handleName
    }

    constructor(firstName: String, lastName: String) {
        this.userName = "$firstName-$lastName"
    }
}


これはコンパニオンオブジェクトを利用して以下のように書き換え可能です。

class User {
    val userName : String

    private constructor(handleName: String) {
        this.userName = handleName
    }

    private constructor(firstName: String, lastName: String) {
        this.userName = "$firstName-$lastName"
    }

    companion object {
        fun handleNamUser(handleName: String) =
                User(handleName)

        fun fullNameUser(firstName: String, lastName: String) =
            User(firstName, lastName)
    }
}


呼び出しコードは以下のようになります

val user1 = User.handleNamUser("handle-001")
println(user1.userName)  // -> handle-001

val user2 = User.fullNameUser("Taro", "Kotlin")
println(user2.userName)  // -> Taro-Kotlin


まとめ

サンプルコードとしては微妙だった気もしますが、コンパニオンオブジェクトによってファクトリメソッド(staticファクトリ)が実現できることがわかりました。



関連エントリ