Runner in the High

技術のことをかくこころみ

AnyValを継承する意味

ScalaでDDDなコードのアプリケーションを作ろうとしているときに UserId など値型はどうするべきか の記事を読み、「専用の値クラスを作る」のパターンでふと 「ここでケースクラスが AnyVal を継承する理由ってなんだ...?」 と思ったので調べた。

case class PersonId(value: Long) extends AnyVal
case class PersonName(value: String) extends AnyVal
case class OrganizationId(value: Long) extends AnyVal

case class Person(id: PersonId, name: PersonName, organizationId: OrganizationId)
// ...

AnyValを継承すると

AnyValを継承すると値オブジェクトになる。値オブジェクトを継承したクラスはひとつの値しかとれない。

// OK
class Melo(val a: Int) extends AnyVal

// NG
class Melo(val a: Int, val n: String) extends AnyVal

AnyVal を使うことによって 実行時のオブジェクト割り当てを回避することができる ようになる。具体的には上の例でいうと Melo クラスはコンパイル時は Melo クラスだが、実行時は Int として解釈される(アンボクシング)

だが、パターンマッチングなどで型検査が必要になると、 Int としてアンボクシングされた値が再び Melo としてボクシングされることになるため、パフォーマンスに影響を与える。

結論

雑に言うと AnyValを継承したクラスを使うとパフォーマンスが向上する っぽい。

個人的にはDDDにおける値オブジェクトをコードで表現するにあたって、もし 引数がひとつしかない のであれば、どんなケースでも AnyVal を継承しない理由がないように思える... というか、調べていて値クラスという名前がたまたまDDDっぽいだけであって、これならべつにエンティティの実装だって可能なら AnyVal 継承クラスでえんちゃうの、と思ってしまった。

ただ、例えばバリデーションロジックで 「〜文字以下かどうかをチェックする」 みたいなビジネスルールがあったとき、クラスの中に MAXIMUM_LENGTH みたいな定数を宣言するのは普通だが AnyVal を継承しているクラスの中で val による宣言ができないという制約がある。

コレに関しては、以下のようなメソッドを定義してしまえばいいのでは、という同期からのアイデアをもらったが、果たしてアリなのか...?

class Text(value: String) extends AnyVal {
  def MAXIMUM_LENGTH = 100
}

参考文献