Scala:是hashCode()&如果在非平凡的超类中尚未指定的情况下,仅生成case类的equals()?

时间:2017-07-08 19:39:32

标签: scala inheritance

我试图定义一个抽象类" Vertex"通过抽象字段" id"识别所有子类的所有实例(在集合/映射/不同等中),即使子类是定义其他单个参数的不同案例类。因此,我希望实现所有子类调用抽象超类中定义的hashCode() & equals()。换句话说,对于继承自" Vertex"我想禁用Scala编译器为case类生成的自动覆盖。

根据以下示例,如果案例类已经在非平凡的超类中明确定义了这些函数,那么Scala编译器似乎不为case类生成hashCode() & equals()

abstract class AbstractVertex {
  val id: String

  // identify only by id, ignoring any additional fields of case classes

  override def hashCode(): Int = id.hashCode
  override def equals(obj: scala.Any): Boolean = {
    obj match {
      case other: AbstractVertex => other.id == id
      case _ => false
    }
  }
}

case class VertexType1(id: String, data: String) extends AbstractVertex

case class VertexType2(id: String, data: String) extends AbstractVertex

case class VertexType3(id: String, data: String) { // NOT an AbstractVertex
  // manually provide our own hashCode and equals
  override def hashCode(): Int = id.hashCode
  override def equals(obj: scala.Any): Boolean = {
    obj match {
      case other: VertexType3 => other.id == id
      case _ => false
    }
  }
}

case class VertexType4(id: String, data: String) // NOT an AbstractVertex

object Playground extends App {
  // create several vertices, all of the same id
  val v1a = VertexType1("x", "some")
  val v1b = VertexType1("x", "other")
  val v2a = VertexType2("x", "some")
  val v2b = VertexType2("x", "other")
  val v3a = VertexType3("x", "some")
  val v3b = VertexType3("x", "other")
  val v4a = VertexType4("x", "some")
  val v4b = VertexType4("x", "other")
  val v4c = VertexType4("x", "some")

  println(Set(v1a, v1b, v2a, v2b).size) // gives 1
  println(Set(v1a, v1b).size) // gives 1
  println(Set(v2a, v2b).size) // gives 1
  println(Set(v3a, v3b).size) // gives 1
  println(Set(v4a, v4b, v4c).size) // gives 2

  println(v1a == v1b) // gives true
  println(v1a == v2a) // gives true
  println(v1a == v3a) // gives false
  println(v1a == v4a) // gives false
  println(v4a == v4b) // gives false
  println(v4a == v4c) // gives true
}

因此,行为符合预期。更重要的是,通过scalac -Xprint:typer Playground.scala | grep "hashCode" -C 5编译上述代码段会显示hashCode()仅为VertexType4生成VertexType3,而是VertexType1明确定义,而VertexType2hashCode()不生成这些内容,因此会回退到AbstractVertex中定义的equals()。类似于hashCode() & equals()

所以一切都按预期工作正常。但是,由于这是Scala编译器如何处理为案例类生成{{1}}的一个非常微妙的细节,我想知道"稳定"这个行为是。不同的Scala版本之间经常会有很多变化,因此将来可能会中断。我可以依靠这种行为吗?例如,是否有一些官方票证,这种行为是明确指定的?

1 个答案:

答案 0 :(得分:3)

这是记录在案的行为。 Section §5.3.2 of the Scala specification州(强调我的):

  

每个case类隐式覆盖类scala.AnyRef 的某些方法定义,除非在case类本身中已经给出了相同方法的定义,或者在某个基类中给出了相同方法的具体定义。案例类与AnyRef不同。特别是:

     
      
  • 方法等于:(任意)布尔值是结构相等,如果它们都属于有问题的案例类,并且它们具有相等(相对于等于)构造函数参数(仅限于类的元素),则两个实例相等,即第一个参数部分。)

  •   
  • 方法hashCode:Int计算哈希码。如果数据结构成员的hashCode方法将相等(相对于等于)值映射为相等的哈希码,则case类hashCode方法也会这样做。

  •   
  • 方法toString:String返回一个字符串表示,其中包含类及其元素的名称。

  •