简体   繁体   中英

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!

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM