Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala case class hierarchy

I got some difficulties designing my case classes. A simplified version looks like:

abstract class Base(s: Option[String]) {
   //code
}

case class CaseClass(s: Option[String] = None) extends Base(s) {
    //code
}

And I have a method where I want to do something like:

  def method(base : Base) = {
     //code
     base copy (s = Some("string"))
  }

Of course I get:

 value copy is not a member of Base

So what I want to do is create a new instance based on my base class (which is not a case class). Obviously one can not do this. But how would you solve this in a elegant way?

Thanks in advance!

like image 551
tbruhn Avatar asked Nov 07 '10 19:11

tbruhn


People also ask

What is the difference between class and case class in Scala?

A class can extend another class, whereas a case class can not extend another case class (because it would not be possible to correctly implement their equality).

What are case classes in Scala?

What is Scala Case Class? A Scala Case Class is like a regular class, except it is good for modeling immutable data. It also serves useful in pattern matching, such a class has a default apply() method which handles object construction. A scala case class also has all vals, which means they are immutable.

Can case class extend another case class?

The answer is simple: Case Class can extend another Class, trait or Abstract Class. Create an abstract class which encapsulates the common behavior used by all the classes inheriting the abstract class.

Are Case classes singleton?

A case object, on the other hand, does not take args in the constructor, so there can only be one instance of it (a singleton like a regular scale object is). A case object is a singleton case class.


2 Answers

If you parameterize your base class and also define the abstract copy method there, you can have the subclasses return instances of their own types from the copy method. In this case, you want CaseClass to return a CaseClass, presumably.

abstract class Base[T](s: Option[String]) {
  def copy(in: Option[String]) : T
}

case class CaseClass(s: Option[String]) extends Base[CaseClass](s) {
  def copy(in: Option[String]) = CaseClass(in)
}

case class OtherClass(s: Option[String]) extends Base[OtherClass](s) {
  def copy(in: Option[String]) = OtherClass(in)
}

def method[T <: Base[T]](base: T) : T = {
  base.copy(Some("String"))
}


scala> method(CaseClass(None))
res1: CaseClass = CaseClass(Some(String))

scala> method(OtherClass(Some("hi")))
res2: OtherClass = OtherClass(Some(String))

Other subclasses of Base would return their own types. The type parameter on #method is defined with an upper bound of Base[T]. This means that T must be any sub-type of Base[T] and is what allows you to supply instances of CaseClass and OtherClass as parameters to that method.

like image 129
Collin Avatar answered Nov 03 '22 11:11

Collin


The behavior you're trying to achieve is not implementable. copy method of a case class is autogenerated by the compiler, and once you add a method called copy to your implementation, compiler will not generate any sugar.

You can reimplement copy with traits, but it will not be as flexible as the generated one (you will have to update the base trait, copy and method implementations every time the field-set of a case class changes):

sealed trait Base[T] {
   val s: Option[String]
   def copy(s: Option[String]) : T
}

case class CaseClass(override val s: Option[String] = None) extends Base[CaseClass] {
    override def copy(s: Option[String]) = CaseClass(s)
}

def method[T <: Base[T]](base : Base[T]) = base copy (s = Some("strng"))

Alternatively, you can implement method as follows:

case class CaseClass(s: Option[String] = None)

def method[X <: {def copy(s: Option[String]):X}](base : X) = 
    base copy(s = Some("string"))

scala> method(CaseClass())
res4: CaseClass = CaseClass(Some(string))

Thus you won't need Base trait, and reduce the number of alterations, if your case classes change.

like image 45
Vasil Remeniuk Avatar answered Nov 03 '22 10:11

Vasil Remeniuk