我试图定义一个抽象类" 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
明确定义,而VertexType2
和hashCode()
不生成这些内容,因此会回退到AbstractVertex
中定义的equals()
。类似于hashCode() & equals()
。
所以一切都按预期工作正常。但是,由于这是Scala编译器如何处理为案例类生成{{1}}的一个非常微妙的细节,我想知道"稳定"这个行为是。不同的Scala版本之间经常会有很多变化,因此将来可能会中断。我可以依靠这种行为吗?例如,是否有一些官方票证,这种行为是明确指定的?
答案 0 :(得分:3)
这是记录在案的行为。 Section §5.3.2 of the Scala specification州(强调我的):
每个case类隐式覆盖类scala.AnyRef 的某些方法定义,除非在case类本身中已经给出了相同方法的定义,或者在某个基类中给出了相同方法的具体定义。案例类与AnyRef不同。特别是:
方法等于:(任意)布尔值是结构相等,如果它们都属于有问题的案例类,并且它们具有相等(相对于等于)构造函数参数(仅限于类的元素),则两个实例相等,即第一个参数部分。)
方法hashCode:Int计算哈希码。如果数据结构成员的hashCode方法将相等(相对于等于)值映射为相等的哈希码,则case类hashCode方法也会这样做。
方法toString:String返回一个字符串表示,其中包含类及其元素的名称。