While looking for something else, quite out of mere coincidence I stumbled upon few comments about how diabolical case class inheritance is. There was this thing called ProductN
, wretches and kings, elves and wizards and how some kind of a very desirable property is lost with case classes inheritance. So what is so wrong with case class inheritance ?

- 21,844
- 13
- 44
- 68
2 Answers
One word: equality
case
classes come with a supplied implementation of equals
and hashCode
. The equivalence relation, known as equals
works like this (i.e. must have the following properties):
- For all
x
;x equals x
istrue
(reflexive) - For
x
,y
,z
; ifx equals y
andy equals z
thenx equals z
(transitive) - For
x
,y
; ifx equals y
theny equals x
(symmetric)
As soon as you allow for equality within an inheritance hierarchy you can break 2 and 3. this is trivially demonstrated by the following example:
case class Point(x: Int, y: Int)
case class ColoredPoint(x: Int, y: Int, c: Color) extends Point(x, y)
Then we have:
Point(0, 0) equals ColoredPoint(0, 0, RED)
But not
ColoredPoint(0, 0, RED) equals Point(0, 0)
You might argue that all class hierarchies may have this problem, and this is true. But case classes exist specifically to simplify equality from a developer's perspective (among other reasons), so having them behave non-intuitively would be the definition of an own goal!
There were other reasons as well; notably the fact that copy
did not work as expected and interaction with the pattern matcher.

- 133,303
- 56
- 317
- 449
-
And what about a little elaboration :) ? – Ashkan Kh. Nazary Jun 22 '12 at 15:15
-
2It seems like such an asymmetric equivalence would be a useful thing in the OO paradigm, in the same way that at the type level a `ColoredPoint` is-a `Point` but not vice-versa. Might have to call it something other than `equals` though... maybe `subEquals`? – Luigi Plinge Jun 22 '12 at 18:58
-
@LuigiPlinge perhaps `canReplace`, `supersedes`, `specifies`, or `overrides` for the reverse relationship? Anything to indicate the `>=`-ness (or `>:` if you like) of it. It seems much easier for me to name it in terms of `>=` rather than `<=`. – Dan Burton Jun 22 '12 at 22:26
-
On second thoughts, such a thing would be tricky (impossible?) to implement due to the possibility of upcasting, so maybe it's not such a great idea – Luigi Plinge Jun 22 '12 at 23:52
-
This example is not compilable in 2.10.2 – expert Aug 03 '13 at 07:22
-
2a generic equals is trivially easy to implement that would satisfy equality, make the class a member of the comparison. the copy thing looks like its just a bug, and interaction with the pattern matcher should work, as it does for non case class hierarchies. – aepurniet Jun 18 '15 at 16:11
-
Exactly, ColoredPoint.class != Point.class? – Lodewijk Bogaards Jul 15 '15 at 07:07
-
1`case class` only gets `equals` when none of its parents override it, so in this case `ColoredPoint` would use `Point`'s `equals`/`hashCode` (I don't know if this was already the case in 2012) which is symmetric (and reflective and transitive). You could argue `ColoredPoint(0, 0, RED) == ColoredPoint(0, 0, GREEN)` is unintuitive, and I'd agree, but the problem is not with case class inheritance: you have exactly the same problem if `Point` is a non-`case` class overriding `equals`. `copy` is more of a problem. – Alexey Romanov Aug 24 '16 at 09:24
-
Would anybody extend this answer by providing the best practice solution to the ColoredPoint problem, please? – Jul 01 '17 at 15:54
That is not overall true. And this is worse than lie.
As mentioned by aepurniet in any case class successor which constricts a definition area must redefine the equality because pattern matching must work exactly as equality (if try to match Point
as ColoredPoint
then it will not matched since color
is not exists).
That give understanding to how the equality of case class hierarchy could be implemented.
case class Point(x: Int, y: Int)
case class ColoredPoint(x: Int, y: Int, c: Color) extends Point(x, y)
Point(0, 0) equals ColoredPoint(0, 0, RED) // false
Point(0, 0) equals ColoredPoint(0, 0, null) // true
ColoredPoint(0, 0, RED) equals Point(0, 0) // false
ColoredPoint(0, 0, null) equals Point(0, 0) // true
Eventually it is possible to satisfy requirements of the equality relation even for case class successor (without overriding of equality).
case class ColoredPoint(x: Int, y: Int, c: String)
class RedPoint(x: Int, y: Int) extends ColoredPoint(x, y, "red")
class GreenPoint(x: Int, y: Int) extends ColoredPoint(x, y, "green")
val colored = ColoredPoint(0, 0, "red")
val red1 = new RedPoint(0, 0)
val red2 = new RedPoint(0, 0)
val green = new GreenPoint(0, 0)
red1 equals colored // true
red2 equals colored // true
red1 equals red2 // true
colored equals green // false
red1 equals green // false
red2 equals green // false
def foo(p: GreenPoint) = ???

- 1
- 1

- 436
- 3
- 10