I know that you're not allowed to inherit from case classes but how would you do when you really need to? We have two classes in a hierarchy, both contain many fields, and we need to be able to create instances of both. Here's my options:
What should I do? Isn't it quite a common problem?
Yes this is quite a recurrent problem, what I would suggest is to create a trait with all parent properties, create a case class which just implements it and then another one which inherits of it with more properties.
sealed trait Parent {
/* implement all common properties */
}
case class A extends Parent
case class B extends Parent {
/*add all stuff you want*/
}
A good way of seeing it is a tree, traits are nodes and case classes are leaves.
You can use a trait or an abstract class depending on your needs for the parent. However, avoid using a class because you would be able to create instances of it, which would not be elegant.
EDIT: As suggested in comments, you can seal the trait in order to have exceptions at compilation if not all case classes are covered in a pattern matching. It is for example explained in chapter 15.5 of "Programming in Scala"
What about replacing inheritance with delegation?
If your two classes in the hierarchy have many shared fields, then delegation could reduce the amount of boilerplate code? Like so:
case class Something(aa: A, bb: B, cc: C, payload: Payload)
sealed abstract class Payload
case class PayloadX(xx: X) extends Payload
case class PayloadY(yy: Y) extends Payload
And then you would create Something
instances like so:
val sth1 = Something('aa', 'bb', 'cc', PayloadX('xx'))
val sth2 = Something('aa', 'bb', 'cc', PayloadY('yy'))
And you could do pattern matching:
sth1 match {
case Something(_, _, _, PayloadX(_)) => ...
case Something(_, _, _, PayloadY(_)) => ...
}
Benefits: (?)
When you declare PayloadX and PayloadY, you don't have to repeat all fields in Something.
When you create instances of Something(... Payload(..))
, you can reuse code that creates Something
, both when you create a Something(... PayloadX(..))
and ...PayloadY
.
Drawbacks: (?)
Perhaps PayloadX
and Y
in your case are actually true subclasses of Something
, I mean, in your case, perhaps delegation is semantically wrong?
You would have to write something.payload.whatever
instead of simply something.whatever
(I suppose this might be good or bad depending on your particular case?)
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