KotlinのValue Class

Kotlin 1.5からStableになる value class についてまとめます。

Photo by Kira auf der Heide on Unsplash

Kotlin 1.2.30からあった inline class がこれまではExperimental/Betaでしたが、1.5でStableになり value class に変更になります。inline class もまだ使えますが警告が表示されます。

この value class についてまとめておきます。

構文

@JvmInline
value class UserId(val id: Int)

構文としては data class に似ています。 value class というのを使って宣言します。

value class には以下の制約があります。

  • 単一のプロパティしか持てない
  • mutableなプロパティを持てない ( var を使ったプロパティが宣言できない)
  • 参照の比較はできない ( === を使った比較ができない)
  • Javaから直接使用できない (少し手を加える必要がある)

@JvmInline アノテーションですが、これはJava/JVMにも同様の機能を追加することが予定されていて (Project Valhalla) 、これが実装後に@JvmInlineアノテーションが不要になる予定です。

詳しくは以下のドキュメントを読んでみてください。

また、このアノテーションはJVM Backendのときのみ必要で、Kotlin/JSでは不要です。

用途

時々、ある型をラップして新しく意味のある型をつくりたいときがあります。DDDがわかる方はValue Objectと言ったほうが早いかもです。

例えば、色を表現するデータの型がIntだとして、これをそのままIntで扱うと問題があるため、Color型として新たに定義したい場合です。

@JvmInline
value class Color(val rgb: Int)

これを見ると typealiasdata class でも良さそうだと思いますが、 value class には様々なメリットがあります。

typealias を使った場合は問題になることがあります。以下のように、引数に typealias の型を使ってる場合をみてみます。

typealias はあくまでaliasなので、普通のIntとして扱うこともできてしまいます。

value class の場合は、Intと取り違えをすることなくコードを書くことができます。

data class は実際のクラスを作ってしまうためパフォーマンスに影響が少なからずあります。

value class の場合は、コンパイル時に可能な限りプリミティブな値としてバイトコードを生成するのでパフォーマンスに影響がありません。

IntelliJなどでBytecodeからDecompileすると以下のように引数が int として扱われることを確認できます。

// コンパイル後
public static final void applyColor_M0a6fYQ(int color) {
}

Javaから使用するには

value class を引数にとっているメソッドをそのままJavaから使用することはできません。

以下のようにIntをラップした value class が複数あり、さらにオーバーロードした関数を作った場合をみてみます。

このコードをコンパイルすると、以下のようにメソッド名が変更されてしまいます。

この変更されたメソッド名を推測しJavaから呼び出すことは難しいです。

これに対応するにはコンパイル後のメソッド名を @JvmName を使って指定することができます。Javaからは指定したメソッド名を使って使用することができます。

参考

Programmer / Gamer / Google Developers Expert for Android, Kotlin / @STAR_ZERO

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store