メソッドを持ったKotlinのEnumをGroovy(Spock)から参照する #groovy #spock #kotlin

概要

  • KotlinはJava同様Enumに振る舞い(メソッド)を持たせることができる
  • Groovyから振る舞いを持ったEnum要素を直接参照するとエラーになる
  • Direct field access operator(@)経由で参照すればOK
  • EnumのValueOfでも参照できる

振る舞いを持たないEnumはうまくいく

enum class Shape {
    CIRCLE,
    TRIANGLE,
    SQUARE,
}

// Enumをフィールドに持つクラス
class Foo(val shape: Shape)
class FooTest extends Specification {
    def "KotlinのEnumを使ったテスト"() {
        given:
        def shape = Shape.CIRCLE

        when:
        def actual = new Foo(shape)

        then:
        actual.shape == Shape.CIRCLE
    }
}

振る舞いをもたせるとエラーになる

// 振る舞いを持ったEnum
enum class Shape {
    CIRCLE {
        override fun sign(): String = "○"
    },
    TRIANGLE {
        override fun sign(): String = "△"
    },
    SQUARE {
        override fun sign(): String = "□"
    };

    abstract fun sign(): String
}

class Foo(val name: String, val shape: Shape)

GroovyRuntimeExceptionが発生してしまう。

groovy.lang.GroovyRuntimeException: Could not find matching constructor for: Foo(java.lang.Class)

どうも振る舞いをもたせるとClassになるようだ。

コンパイル結果を見るとたしかにclassファイルができている。

  • Shape$CIRCLE.class
  • Shape$SQUARE.class
  • Shape$TRIANGLE.class

メソッドも呼び出せない

def "KotlinのEnumメソッドを呼び出すテスト"() {
    expect:
    Shape.CIRCLE.sign() == "○"
}

MissingMethodExceptionが発生してしまう。

Caused by: groovy.lang.MissingMethodException: No signature of method: static Shape$CIRCLE.sign() is applicable for argument types: () values: []

Enumインスタンス経由で呼び出す

Direct field access operator(@)を使って、要素のインスタンスを使うようにするとうまくいきます。

class FooTest extends Specification {
    def "KotlinのEnumを使ったテスト"() {
        given:
        def shape = Shape.@CIRCLE

        when:
        def actual = new Foo(shape)

        then:
        actual.shape == Shape.@CIRCLE
    }

    def "KotlinのEnumメソッドを呼び出すテスト"() {
        expect:
        Shape.@CIRCLE.sign() == "○"
    }
}

valueOf経由で呼び出す

valueOfを使うようにしてもOKです。

class FooTest extends Specification {
    def "KotlinのEnumを使ったテスト"() {
        given:
        def shape = Shape.valueOf('CIRCLE')

        when:
        def actual = new Foo(shape)

        then:
        actual.shape == Shape.valueOf('CIRCLE')
    }

    def "KotlinのEnumメソッドを呼び出すテスト"() {
        expect:
        Shape.valueOf('CIRCLE').sign() == "○"
    }
}

参考