I would like to define a class hierarchy with about 100 case classes
deriving from common base. The types are describing nodes in the AST hierarchy, like this one. I would like to do something along the lines of:
trait Base {
def doCopy: Base
}
trait CloneSelf[T <: CloneSelf[T]] extends Base {
self: T =>
def copy(): T
override def doCopy: T = copy()
}
case class CaseA(a: String) extends Base with CloneSelf[CaseA]
case class CaseB(b: Int) extends Base with CloneSelf[CaseB]
This gives an error, because the existence of my copy
prevents the case classes
from defining the automatic copy. Is there some way how to implement the "clone" doCopy
so that is uses the automatic copy
of those case classes
?
I have found out defining the
doCopy
in eachcase class
is actually less work than defining each class to inherit fromCloneSelf
. The code looks like this:I was surprised to learn that without explicit type on the overridden method the type is inferred by the compiler, therefore the static type of
CaseA("a").doCopy
is the same as ofCaseA("a").copy()
, i.e.CaseA
, notBase
. Adding explicit type for eachcase class
would be probably more obvious, but this would require more work compared to just copy-pasting the same line into each of them. Not that it matters much - when I do copying via thecase class
type, I may use thecopy()
as well. It is only when I have theBase
I need thedoCopy
, therefore declaring it likedef doCopy: Base = copy()
would do little harm.Please do not do that, you should absolutely find a pattern to avoid it! If you want to do this anyway... Try ducktyping:
I cannot test now so this probably won't compile, but you can figure it out by yourself with the general idea!
Edit:
Why having 100 subclasses is evil: imagine you perform one change in the base class, for instance change its name from
Base
toBaseCloning
-> you'll have to change it in EVERY child class (100 changes).How you will avoid that depends on what you want to do with your classes, check creationnal and structural patterns: factory, builder, prototype, flyweight, composite... Always think about "how much work will I have if I change something in the base class? Will it affect all children?"