《Programming in Scala 3rd》阅读笔记

Chapter 30《Object Equality》

2018-08-29  本文已影响1人  liqing151

Scala中的相等性

对象相等性比较的设计

4.不能满足相等关系

两个对象相等必须要满足以下的关系:自反性,对称性,传递性,重现性,任意不为nullvaluenull都是不相等的。目前的设计是满足这些关系的,但如果加入了子类,则情况发生了变化,定义了一个ColoredPoint

class ColoredPoint(x: Int, y: Int, val color: Color.Value)
        extends Point(x, y) { // Problem: equals not symmetric
    override def equals(other: Any) = other match {
        case that: ColoredPoint =>
            this.color == that.color && super.equals(that)
        case _ => false
    }
}

该子类继承了父类的hashCode并在equals中调用了父类的equals方法。p = new Point(1,2),cp = new ColoredPoint(1,2,Yellow)p equals cp可以返回正确的true,但是cp equals p返回的却是false,因为p并不是一个ColoredPoint,不满足对称性。将equals中的对比条件设置得更宽泛些,因此有了以下的设置:

class ColoredPoint(x: Int, y: Int, val color: Color.Value) extends Point(x, y) { // Problem:   equals not transitive
    override def equals(other: Any) = other match {
        case that: ColoredPoint =>
            (this.color == that.color) && super.equals(that)
        case that: Point =>
            that equals this
        case _ =>
            false
    }
}

这样可以满足对称性,但是不满足传递性!p1 == cp1(RED)p1 == cp2(BLUE),但是cp1(RED)cp2(BLUE)并不是相等的。如何能够在类的层级上定义相等函数并保持同级相等关系需要满足的条件?使用canEqual函数

def canEqual(other: Any): Boolean

在任何重写了equals的类中都定义一下这个函数,这个函数定义了哪些obj可以被当做该类的一个实例,从而返回true,否则的话返回false。这个方法会在equals中被调用,从而满足子类进行对比的要求。以下版本是特别严格的比较版本:

class Point(val x: Int, val y: Int) {
  override def hashCode = (x, y).##

  override def equals(other: Any) = other match {
    case that: Point => (that canEqual this) && (this.x == that.x) && (this.y == that.y)
    case _ => false
  }

  def canEqual(other: Any) = other.isInstanceOf[Point]
}

canEqual声明了所有的Point的子类都可以被当做是Point进行比较。对于ColoredPoint的实现则可以

class ColoredPoint(x: Int, y: Int, val color: String) extends Point(x, y) {
  override def hashCode = (super.hashCode, color).##

  override def equals(other: Any) = other match {
    case that: ColoredPoint => (that canEqual this) && super.equals(that) && this.color == that.color
    case _ => false
  }

  override def canEqual(other: Any) =
    other.isInstanceOf[ColoredPoint]
}

如果在父类中重写了equals并定义了canEqual函数,那么在子类中可以决定要不要定义canEqual函数来决定子类对象是否可以和父类相等。如果定义了canEqual,则子类对象不能和父类对象相等;如果没有定义canEqual,则子类继承父类的canEqual,子类对象可以和父类对象相等,这种情况发生在子类对象和父类对象真的可以相等的时候,因为相等具有自反性,child=father,father=child则是必须的。这种情况则最大给予了子类的自由,如果是一个new Point(1,2) {val y=1}这种,其实就是父类,可以不用重写canEqual,但是如果在Point上添加了色彩属性,就一般需要重新覆盖canEqual了。

为参数化的类型定义相等性

制定equals函数的步骤:

制定hashcode的步骤

总结

上一篇下一篇

猜你喜欢

热点阅读