I have an abstract class which I extend and make numerous case classes. Now I want to copy instances of those case classes just changing first parameter, so I use case class' copy
method.
Since I have to do this for all case classes that have been extended from common abstract class, rather than doing it for all, I tried to make it general and made the abstract class a case class.
Then Scala gives me this:
case class Octopus has case ancestor Organism, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes.
Code:
abstract class Organism(legs: Int)
case class Octopus(override val legs: Int, weight: Double, ...)
case class Frog(override val legs: Int, ...)
def clone(o: Organism) = o.copy(legs = -1)
This is what I want to do. But if I can't make the clone
method work, then I will have to do copying for both Octopus
& Frog
.
Any suggestions, to decrease this verbosity?
The answer is simple: Case Class can extend another Class, trait or Abstract Class.
Scala Inheritance When a class inherits from another, it means it extends another. We use the 'extends' keyword for this. This lets a class inherit members from the one it extends and lets us reuse code. The class that extends is the subclass, the child class, or the derived class.
Multiple Inheritance: In Multiple inheritance ,one class can have more than one superclass and inherit features from all parent classes. Scala does not support multiple inheritance with classes, but it can be achieved by traits.
You can't abstract over case class' copy
methods generically. I'd suggest using Lenses from Shapeless or Monocle:
trait Organism { def legs: Int }
// monocle @Lenses uses a macro to generate lenses
@Lenses case class Octopus(override val legs: Int, weight: Double, ...)
extends Organism
@Lenses case class Frog(val legs: Int, ...) extends Organism
def clone[O <: Organism](o: O, legsLens: Lens[O, Int]): O =
legsLens.set(-1)(o)
val myOctopus = Octopus(8, 2.4, ...)
val myFrog = Frog(2, ...)
// use the generated Lenses
val cloneOctopus: Octopus = clone(myOctopus, Octopus.legs)
clone(myFrog, Frog.legs)
Using only standard scala there is no such generic copy method on abstract (super) class: how would it know how all subclasses can be cloned/copied? Especially that new subclasses could be added in the future.
To my knowledge, the two main approaches to implement such abstract method are:
1) make a function that match-case on all subclasses:
def clone(o: Organism) = o match {
case o: Octopus => o.copy(legs = -1)
case f: Frog => f.copy(legs = -1)
}
Then each time a new subclass is added, it needs to be added in this functions. This is most appropriate for use with sealed abstract class.
2) add a makeClone
method to the abstract API (the name clone
being reserved):
abstract class Organism(legs: Int){
def makeClone(legNumber: Int): Organism
}
case class Octopus(legs: Int, weight: Double) extends Organism(legs) {
def makeClone(legNumber: Int) = this.copy(legs = legNumber)
}
Note that while the function in (1) always returns an Organism
, here the method Octopus.makeClone
returns an Octopus
.
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