KotlinのValue Class
Kotlin 1.5からStableになる value class
についてまとめます。
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
@JvmInline
アノテーションですが、これはJava/JVMにも同様の機能を追加することが予定されていて (Project Valhalla) 、これが実装後に@JvmInline
アノテーションが不要になる予定です。
詳しくは以下のドキュメントを読んでみてください。
また、このアノテーションはJVM Backendのときのみ必要で、Kotlin/JSでは不要です。
用途
時々、ある型をラップして新しく意味のある型をつくりたいときがあります。DDDがわかる方はValue Objectと言ったほうが早いかもです。
例えば、色を表現するデータの型がIntだとして、これをそのままIntで扱うと問題があるため、Color型として新たに定義したい場合です。
@JvmInline
value class Color(val rgb: Int)
これを見ると typealias
や data class
でも良さそうだと思いますが、 value class
には様々なメリットがあります。
typealiasとの違い
typealias
を使った場合は問題になることがあります。以下のように、引数に typealias
の型を使ってる場合をみてみます。
typealias
はあくまでaliasなので、普通のIntとして扱うこともできてしまいます。
value class
の場合は、Intと取り違えをすることなくコードを書くことができます。
data classとの違い
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からは指定したメソッド名を使って使用することができます。