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!
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 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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With