Kotlinのvalue classについて

採用はこちら

Kotlinのvalue class(旧称 inline class)は、単一の値を型安全にラップするための軽量なクラスです。

設計目的は明確で、

  • 型安全性を高める
  • 不要なオブジェクト生成を可能な限り避ける
  • ドメインに意味を持たせる

という3点にあります。

目次

基本構文

@JvmInline
value class UserId(val value: String)

重要なポイント

  • 主コンストラクタ(primary constructor)で初期化される単一プロパティが必須
  • JVMでは @JvmInline が必要
  • クラスは暗黙的に final
  • クラス継承は不可(ただしインターフェース実装は可能)

実行時の表現

value classは「常にインライン化される」わけではありません。

より正確に言うと

コンパイラは可能な限り underlying 値(内部プロパティ)として扱うが、状況によってはラッパーとしてボクシングされる。

ボクシングが発生する代表例

  • ジェネリクス型として扱う場合
  • インターフェース型として扱う場合
  • nullable 型として扱う場合

fun <T> printValue(value: T)

ここに UserId を渡すとボクシングされます。

つまり「プリミティブ並み」と言い切るのは正確ではなく、

多くのケースで最適化されるが、常に保証されるわけではない

という理解が正解です。

data classとの違い

比較項目data classvalue class
プロパティ数複数可1つのみ
オブジェクト生成常に生成状況により回避
主目的データ構造型安全なラッパー
継承クラス継承不可

value classは「構造」ではなく「意味付け」に使うものです。

equals / hashCode / toString の挙動

value classは、内部プロパティに基づいて比較されます。

UserId("abc") == UserId("abc") // true

明示的にオーバーライドしない限り、

  • equals
  • hashCode
  • toString

は内部プロパティに委譲される形になります。

参照等価(===)は使用不可

value classは wrapper 表現と underlying 表現の両方を取り得るため、参照等価は意味を持ちません。

そのため

a === b

は使用できません。

これは仕様上の重要ポイントです。

メンバー定義について

value classでも以下が可能です。

  • init ブロック
  • メンバー関数
  • 計算プロパティ
  • 二次コンストラクタ

@JvmInline
value class Email(val value: String) {
    init {
        require(value.contains("@"))
    }

    fun domain(): String =
        value.substringAfter("@")
}

ただし制限もある

backing fieldを持てない

メンバープロパティは backing field を持てません。

そのため

  • lateinit 不可
  • delegated property 不可
  • 状態保持は不可

クラス継承は不可

他のクラスを継承できません。

ただし

interface Printable

@JvmInline
value class UserId(val value: String) : Printable

のようにインターフェース実装は可能です。

typealiasとの決定的な違い

typealias UserId = String

これは単なる別名であり、型は同一です。

一方

@JvmInline
value class UserId(val value: String)

これは完全に別型です。

そのため

fun getUser(id: UserId)

String を直接渡すことはできません。

ここがvalue classの最大の価値です。

実務での活用例

ID型

  • UserId
  • OrderId
  • ProductId

ドメイン型

  • Email
  • PhoneNumber
  • ZipCode

単位型

  • Meter
  • Yen
  • Kilogram

DDD(ドメイン駆動設計)との相性が非常に良く、「意味のある型」を低コストで作れるのが最大のメリットです。

まとめ

value classは

  • 主コンストラクタで初期化される単一プロパティを持つ
  • 可能な場合は underlying 値として扱われる
  • ただし状況によりボクシングされる
  • backing fieldは持てない
  • クラス継承不可、インターフェース実装は可能
  • 参照等価は使用不可
  • equals/hashCodeは内部値に基づく

設計思想としては、

「プリミティブに意味を与えたいが、オブジェクトコストは増やしたくない」

という場面で使うものです。

以上、Kotlinのvalue classについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次