在寻找其他内容的过程中,很偶然地我发现了一些有关case class继承是多么恶魔般的评论。有这个叫做ProductN
的东西,卑鄙和国王、精灵和巫师,以及在case class继承中会失去某种非常理想的属性。那么case class继承有何问题?
在寻找其他内容的过程中,很偶然地我发现了一些有关case class继承是多么恶魔般的评论。有这个叫做ProductN
的东西,卑鄙和国王、精灵和巫师,以及在case class继承中会失去某种非常理想的属性。那么case class继承有何问题?
一个词: equality
case
类提供了equals
和hashCode
的默认实现。这个等价关系被称为equals
,它遵循以下属性:
x
,x equals x
是true
(自反性)x
、y
和z
,如果x equals y
和y equals z
,那么x equals z
(传递性)x
和y
,如果x equals y
,那么y equals x
(对称性)一旦你允许在继承层次结构中使用相等,就可能破坏2和3。下面的示例可以轻松地证明这一点:
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)
但是不是这样
ColoredPoint(0, 0, RED) equals Point(0, 0)
你可能会认为所有类层次结构都可能存在这个问题,这是正确的。但是,case类的存在是为了从开发者的角度简化相等性(以及其他原因),因此让它们表现出不直观的行为将是一个内部错误!
还有其他原因,特别是copy
没有按预期工作和与模式匹配器的交互。
那并不是完全正确的。这比谎言更糟糕。
正如aepurniet所提到的,在任何情况下,限制定义区域的类继承者必须重新定义相等性,因为模式匹配必须与相等性完全一致(如果尝试将Point
作为ColoredPoint
进行匹配,则不会匹配,因为color
不存在)。
这使我们了解了如何实现案例类层次结构的相等性。
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
最终,即使是case类的后继者(无需覆盖相等性),也有可能满足相等关系的要求。
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) = ???
ColoredPoint
是Point
的子类,但反之则不然。也许需要给它取一个不同于equals
的名字... 可能是subEquals
? - Luigi Plingecase class
才会获得equals
,因此在这种情况下,ColoredPoint
将使用Point
的equals
/hashCode
(我不知道这是否已经是2012年的情况),这是对称的(和反射和传递)。你可以认为ColoredPoint(0, 0, RED) == ColoredPoint(0, 0, GREEN)
是不直观的,我也同意,但问题并不在于case class
继承:如果Point
是一个非case
类覆盖equals
,你将遇到完全相同的问题。copy
更成问题。 - Alexey Romanov