Android enumではなくIntDefを使ってdata classを組み立てる


# 環境
Android Studio Arctic Fox | 2020.3.1 Patch 2
Kotlin 1.5.31

# 概要

列挙型の値を使うときに、`@IntDef`を生かしてdata classを組み立ててみます。

# なぜenumではなくIntDefを使うのか

Google I/O 19で、`Build apps for the next billion users` という動画があります。
<iframe width="560" height="315" src="https://www.youtube.com/embed/UPZPpuWNCp8?start=1448" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

この中の、`Reduce your app size`に挙げている項目で、`Avoid enumerations`というものがあります。
一個のenumでファイルのサイズが 1.0～1.4 KB程度増えるとあり、使い方によってはさらに増えるそうです。
このことは、公式のドキュメントにも記載があります。
https://developer.android.com/topic/performance/reduce-apk-size#remove-enums

というわけで、Google wayとしてenumを使わずにIntDefアノテーションで対応をしてみます。

# TypedefアノテーションクラスをJavaで用意する
IntDefを定義するアノテーションクラスをJavaで用意します。

```java
@IntDef({Status.IDLE, Status.PROCESSING, Status.DONE, Status.CANCELLED})
@Retention(RetentionPolicy.SOURCE)
@interface Status {
    int IDLE = 0;
    int PROCESSING = 1;
    int DONE = 2;
    int CANCELLED = 3;
}
```

~~Kotlinで用意しようとすると、後述のアノテーションによる検査が効かなかったです。これではアノテーションの意味がないのでJavaで用意してますが、反映させる方法があったら誰か教えてください。~~

2022/10/23追記
以下の環境ではKotlinでもアノテーション検査が効いていることを確認しています。

```
Android Studio Dolphin | 2021.3.1
Build #AI-213.7172.25.2113.9014738, built on September 1, 2022
Runtime version: 11.0.13+0-b1751.21-8125866 x86_64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
macOS 12.6
GC: G1 Young Generation, G1 Old Generation
Memory: 2048M
Cores: 16
Registry:
    external.system.auto.import.disabled=true
    ide.text.editor.with.preview.show.floating.toolbar=false

Non-Bundled Plugins:
    org.intellij.plugins.markdown (213.6777.22)
    com.github.setial (4.0.2)
    Dart (213.7433)
    com.thoughtworks.gauge (213.5744.125)
    de.ohmesoftware.parcelablegenerator (0.7.1)
    io.flutter (70.2.3)

```


```kotlin
// アノテーションによる検査が効かない
@Retention(AnnotationRetention.SOURCE)
@IntDef(IDLE, PROCESSING, DONE, CANCELLED)
annotation class Status

// Declare the constants
const val IDLE = 0
const val PROCESSING = 1
const val DONE = 2
const val CANCELLED = 3
```

# data classでIntDefな値を提供する
以下のコードのように、コンストラクタの引数には`@setparam` をつけたprivate varなパラメータを用意し、別途`@get`で公開用のvalなパラメータを用意します。

```kotlin
data class MyData(@setparam:Status private var _state: Int) {
    @get:Status
    val state: Int = _state
}
```

# IntDefな値を提供するdata classを使う
全体のコード

```kotlin
        val data = MyData(CANCELLED)
        val state = data.state
        val i = when(state) {
            Status.CANCELLED -> {
                1
            }
            Status.DONE -> {
                2
            }
            Status.IDLE -> {
                3
            }
            Status.PROCESSING -> {
                4
            }
            else -> 5
        }
        print(i)
```

アノテーションクラスをJavaで提供していると、data classの`@get`の効果で、画像のようにIntDefの定数を補完するための項目が出てきます。これを使うことでenumと同じ処理分岐が可能になります。
![スクリーンショット 2021-10-06 0.24.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62305/c740f0f5-9380-3a01-b0d6-e5928c398253.png)


同様に、`@setparam`の効果で、data classのコンストラクタに適当な数値を入れると補完候補に`@IntDef`で定義した値が選択できます。
![スクリーンショット 2021-10-06 0.27.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62305/b172c38f-c23f-78e3-d43a-451b809a7dc8.png)

# まとめ

~~enumのほうが作りやすい~~
つかおう！IntDef！