Scala case类继承有什么问题?

Scala case类继承有什么问题?,scala,inheritance,case-class,Scala,Inheritance,Case Class,在寻找其他东西的时候,完全出于巧合,我偶然发现了一些关于case类继承是多么邪恶的评论。有一个东西叫做ProductN,坏蛋和国王,精灵和巫师,以及case类继承如何丢失某种非常理想的属性。那么case类继承有什么问题呢?一个词:相等 case类附带了equals和hashCode的实现。等价关系称为equals,其工作原理如下(即必须具有以下属性): 对于所有xx等于x为true(自反) 对于x,y,z;如果x等于y和y等于z则x等于z(可传递) 对于x,y;如果x等于y,则y等于x(对称)

在寻找其他东西的时候,完全出于巧合,我偶然发现了一些关于case类继承是多么邪恶的评论。有一个东西叫做
ProductN
,坏蛋和国王,精灵和巫师,以及case类继承如何丢失某种非常理想的属性。那么case类继承有什么问题呢?

一个词:相等

case
类附带了
equals
hashCode
的实现。等价关系称为
equals
,其工作原理如下(即必须具有以下属性):

  • 对于所有
    x
    x等于x
    true
    (自反)
  • 对于
    x
    y
    z
    ;如果
    x等于y
    y等于z
    x等于z
    (可传递)
  • 对于
    x
    y
    ;如果
    x等于y
    ,则
    y等于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)
    
    但不是

    您可能会认为所有的类层次结构都可能有这个问题,这是事实。但是case类的存在是为了从开发人员的角度简化平等性(除其他原因外),因此让它们以非直观的方式运行将是自己目标的定义



    还有其他原因,;值得注意的是,和。

    这一事实并非完全正确。这比撒谎更糟糕

    正如所提到的,在任何情况下,压缩定义区域的类继承者必须重新定义相等,因为模式匹配必须与相等完全相同(如果尝试将
    匹配为
    彩色点
    ,则它将不匹配,因为
    彩色
    不存在)

    这让我们了解如何实现case类层次结构的平等性

    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类继承者,也可以满足等式关系的要求(而不重写等式)


    还有一点细化呢:)?在OO范式中,这种不对称的等价似乎是一件有用的事情,就像在类型级别a
    ColoredPoint
    is-a
    Point
    一样,但反之亦然。可能不得不称之为除<代码>等于<代码>之外的其他名称,但是。。。可能
    子等于
    ?@Luigipling可能
    可以替换
    替代
    指定
    ,或者
    覆盖
    反向关系?任何表示它的
    =
    -ness(或者
    >:
    ,如果您愿意)的内容。对我来说,用
    =
    来命名它似乎比用
    来命名要容易得多。一个泛型equals非常容易实现,它将满足equality,使类成为比较的一个成员。复制的东西看起来只是一个bug,与模式匹配器的交互应该可以工作,就像它对非case类层次结构所做的那样。
    case类
    只有在没有父类覆盖它时才会得到
    equals
    ,因此在这种情况下,
    ColoredPoint
    将使用
    equals
    hashCode
    (我不知道2012年的情况是否已经如此)它是对称的(反射的和传递的)。你可以争论
    ColoredPoint(0,0,红色)==ColoredPoint(0,0,绿色)
    是不直观的,我也同意,但问题不在于case类继承:如果
    Point
    是一个非
    case
    类重写
    equals
    copy
    是一个更大的问题,那么您就有完全相同的问题。
    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 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) = ???