kotlinでEnumをもっと便利に使いたい!

はじめに

kotlinはJetbrains社が開発したJavaとの運用互換性100%と謳われるプログラミング言語です。
そう謳うだけあってEnumもJavaとそっくりに作られています。

本題

さて、このEnumを実際に使うときによくある要求が、文字列からEnumを取得、整数(ordinal)からEnumを取得するパターンです。

文字列からEnumを取得する場合は、Java同様valueOf(name)という関数が提供されているのでこれを使えば良さそうです。

しかし、この関数は文字の大文字小文字を区別するので、コーディング規約に則ってコーディングをしているとアッパースネークケースの文字列を利用してEnumを取得せねばなりません。
外部のAPIを利用していたりするといつもアッパースネークケースというわけにはいかず少し手間でしょう。

また、整数から対応するEnumを取得するケースでは、特別関数が用意されているわけではないので、次のようなコードを自分で書く必要が出てきます。

enum class MyEnum {
    ONE,
    TWO,
    OTHER
}

MyEnum.values().first { it.ordinal == 0 } // MyEnum.ONEが取れる

values関数でEnumの全要素のArrayが取得出来るので、そこから対応する要素を取り出しています。

さらに、kotlinの場合、Enumはクラスなので独自のメンバを持たせることもできます。
例えばこんな感じですね。

enum class MyEnum(val raw: Int) {
    ONE(1),
    TWO(2),
    OTHER(3)
}

この独自のメンバを利用して要素を取得したいケースでは整数の時、同様こんな感じになります。

MyEnum.values().first { it.raw == 1 } // MyEnum.ONEが取れる

これもパッと見で何がやりたいのかわかりづらいですね。 おまけにEnumを使うたびにこの様なコードを書くのは手間ですね。

そこで、これらのEnumを使うときによくあるパターンを関数として提供してくれるライブラリを作りました!

kotlin-enum-extensions

https://github.com/ChanTsune/kotlin-enum-extensions

このライブラリで提供する関数は大別すると次の3つです。

  • 文字列から対応するEnumを取得する関数(ignoreCaseをtrueにすると大文字小文字を無視して対応する要素を取得)

  • 整数から対応するEnumを取得する関数

  • 対応するEnumの条件を関数として渡して取得できる関数

さらにそれぞれ、デフォルト値を設定できるenumValueOf()と対応するEnumが見つからなかった場合にnullを返してくれるenumValueOfOrNull()が有ります。

// 文字列から取得するパターン
enumValueOf<MyEnum>("two", ignoreCase=true) // MyEnum.TWO
enumValueOf<MyEnum>("six", default=MyEnum.TWO, ignoreCase=true) // MyEnum.TWO
enumValueOfOrNull<MyEnum>("not exist") // null

// 整数から取得するパターン
enumValueOf<MyEnum>(1) // MyEnum.TWO
enumValueOf<MyEnum>(-1, default=MyEnum.TWO) // MyEnum.TWO
enumValueOfOrNull<MyEnum>(-1) // null

// 関数で条件を指定するパターン
enumValueOf<MyEnum> { it.raw == 2 } // MyEnum.TWO
enumValueOf<MyEnum>(default = MyEnum.ONE) { it.raw == 9 } // MyEnum.ONE
enumValueOfOrNull<MyEnum> { it.raw == 9 } // null

全部で9つです。

また、自身で定義するEnumの場合、IDEの補完の恩恵を受けやすくする為の小技も用意してあります。

enum class MyEnum { 
    ONE,
    TWO,
    OTHER;

    companion object : EnumExtension<MyEnum>
}
MyEnum.valueOfOrNull("one", ignoreCase = true) // MyEnum.ONE

EnumのコンパニオンオブジェクトにEnumExtensionインターフェースを実装しています。
このインターフェイスは拡張関数の受け皿なので特に何かを追加で実装する必要はありません。
なぜ、このような周りくどいことをやっているかは本題ではないので詳しくは記述しません。

詳しく知りたい方はこちらをご覧ください。

このライブライブラリの提供する関数を利用すると、先ほどのメンバを利用してEnumを取得するパターンは次のように書けます。

MyEnum.valueOf { it.raw == 1 } // MyEnum.ONEが取れる

最後に

利用可能になる関数はわずかですが毎回記述する事を考えると結構な手間なのでライブラリとして切り出して公開してみました。

以上、ほんの少しEnumの取り回しが良くなるライブラリの紹介でした。