Groovy(Spock)からKotlinのCompanion Objectを呼び出す #groovy #spock #kotlin

概要

最近はJavaではなくKotlinを書いていますが、Spockが好きすぎるのでテストはGroovyで書いたりします。

KotlinのCompanion Objectをテストするときに、Groovy側から呼び出す方法です。

Companion Object

Kotlinではクラスに静的なメソッドを定義する時はCompanion Objectを使います。

Javaのstaticみたいなものと自分は理解しています。

class Sample {
    companion object {
        // 大文字にするメソッド
        fun toUpper(s: String): String = s.toUpperCase()
    }
}

こうするとKotlin側では Sample.toUpper("hoge") のように呼び出せるようになります。

Groovy側での呼び出し方

GroovyではCompanionのメソッドを呼び出すには Direct field access operator を使います。

class SampleSpec extends Specification {
    def 'hogeが大文字HOGEになる'() {
        when:
        def actual = Sample.@Companion.toUpper('hoge')

        then:
        actual == 'HOGE'
    }
}

Groovyからの呼び出しにはCompanionオブジェクトを経由する必要があり、なおかつダイレクトアクセスである必要があります。

ダイレクトアクセスする方法は、アクセスするフィールドの前に @ を付けるだけです。

Companionを経由しない Sample.toUpper('hoge') や、通常のフィールドアクセス Sample.Companion.toUpper('hoge') では MissingMethodException が発生します。

ダイレクトアクセスと通常のフィールドアクセスの違いですが、Groovyのフィールドアクセスは暗黙的にGetterが呼ばれるのに対し、ダイレクトアクセスの場合Getterは呼ばれずフィールドの直アクセスになります。

CompanionオブジェクトにはGetterがないので、直アクセスする必要があるのかなと想像しています。

JvmStatic

JvmStaticアノテーションを使えば、Groovyからでも直接メソッドを呼び出せるようになります。

@JvmStatic
fun toUpper(s: String): String = s.toUpperCase()

ただ、テストコードのためにプロダクションコードに手を入れることになるので今回のようなケースではおすすめしません。

参考

Problems about accessing Kotlin companion object in Groovy? - Stack Overflow The Apache Groovy programming language - Operators