sealed abstract case classがすごい

sealed abstract case classなるものがある。別のことを調べてたらこちらの記事(のコメント欄)で見掛けて感動してしまった。

軽く触れると、newできなくなり、case classによって自動で生成されるcopyメソッド、そしてコンパニオンオブジェクトのapplyメソッドが生成されなくなる、というもの。その上でcase classの他の特性を持つ。

これ、インスタンスの生成に関する手段のみが綺麗に潰されているので、バリデーションを挟むファクトリだったりを強制できるのが嬉しい。

私自身は今まで以下のようにしていた。

trait Person {
  def name: String
  
  def age: Int
}

private case class PersonImpl(name: String, age: Int) extends Person

object Person {
  def apply(name: String, age: Int): Person = {
    // ...
    PersonImpl(name, age)
  }
}

こんな風にtraitでインタフェースのみ定義し、その実装をcase classにする。これでequalsメソッドなどは自動で定義してくれる。で、traitの方のコンパニオンオブジェクトで生成してあげる。もちろん実装であるcase class自体は隠しちゃう。

しかしこれ、unapplyメソッドなどは自前で定義しなくちゃならないし、toStringメソッドは実装クラス(この場合はPersonImpl)のものなので、想定した結果にならないと、微妙なとこもあった。

ので、まあケースに応じはするわけだが、sealed abstract case classは有用そうだなと思う。