Scala case class inheritance

Andrea picture Andrea · Oct 3, 2012 · Viewed 95.8k times · Source

I have an application based on Squeryl. I define my models as case classes, mostly since I find convenient to have copy methods.

I have two models that are strictly related. The fields are the same, many operations are in common, and they are to be stored in the same DB table. But there is some behaviour that only makes sense in one of the two cases, or that makes sense in both cases but is different.

Until now I only have used a single case class, with a flag that distinguishes the type of the model, and all methods that differ based on the type of the model start with an if. This is annoying and not quite type safe.

What I would like to do is factor the common behaviour and fields in an ancestor case class and have the two actual models inherit from it. But, as far as I understand, inheriting from case classes is frowned upon in Scala, and is even prohibited if the subclass is itself a case class (not my case).

What are the problems and pitfalls I should be aware in inheriting from a case class? Does it make sense in my case to do so?

Answer

Malte Schwerhoff picture Malte Schwerhoff · Oct 3, 2012

My preferred way of avoiding case class inheritance without code duplication is somewhat obvious: create a common (abstract) base class:

abstract class Person {
  def name: String
  def age: Int
  // address and other properties
  // methods (ideally only accessors since it is a case class)
}

case class Employer(val name: String, val age: Int, val taxno: Int)
    extends Person

case class Employee(val name: String, val age: Int, val salary: Int)
    extends Person


If you want to be more fine-grained, group the properties into individual traits:

trait Identifiable { def name: String }
trait Locatable { def address: String }
// trait Ages { def age: Int }

case class Employer(val name: String, val address: String, val taxno: Int)
    extends Identifiable
    with    Locatable

case class Employee(val name: String, val address: String, val salary: Int)
    extends Identifiable
    with    Locatable