What is *so* wrong with case class inheritance?
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.
Comments
-
Ashkan Kh. Nazary about 2 years
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 ? -
Ashkan Kh. Nazary about 12 yearsAnd what about a little elaboration :) ?
-
Luigi Plinge about 12 yearsIt 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-aPoint
but not vice-versa. Might have to call it something other thanequals
though... maybesubEquals
? -
Dan Burton about 12 years@LuigiPlinge perhaps
canReplace
,supersedes
,specifies
, oroverrides
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<=
. -
Luigi Plinge about 12 yearsOn 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
-
expert almost 11 yearsThis example is not compilable in 2.10.2
-
aepurniet about 9 yearsa 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.
-
Lodewijk Bogaards almost 9 yearsExactly, ColoredPoint.class != Point.class?
-
Alexey Romanov almost 8 years
case class
only getsequals
when none of its parents override it, so in this caseColoredPoint
would usePoint
'sequals
/hashCode
(I don't know if this was already the case in 2012) which is symmetric (and reflective and transitive). You could argueColoredPoint(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 ifPoint
is a non-case
class overridingequals
.copy
is more of a problem. -
Admin about 7 yearsWould anybody extend this answer by providing the best practice solution to the ColoredPoint problem, please?