问题
在Scala中,有没有办法编写一个match
子句,它通过它的类匹配一个对象,但是不匹配任何扩展类?
动机
它可能看起来很迂腐..它是
无论如何,当我看到IntelliJ生成的equals
函数时,我想到了它:
class Position(val x: Float, val y: Float) {
def canEqual(other: Any): Boolean = other.isInstanceOf[Position]
override def equals(other: Any): Boolean = other match {
case that: Position =>
(that canEqual this) &&
x == that.x &&
y == that.y
case _ => false
}
// generated hashCode omitted for brevity
}
在这里,我们可以看到other
类匹配为Position
,然后canEqual
用于在匹配上设置上限。也就是说,other
仍可以是Position3D
,即使Position
和x
坐标相同,也不应等于y
这是有道理的,但我想知道某种形式的Scala巫术是否会让我们明确地匹配Position
类而不是子类。
答案 0 :(得分:1)
尝试:
other match {
case that: Position if that.getClass == classOf[Postion] => // or just getClass
(that canEqual this) &&
x == that.x &&
y == that.y
case: that: Postion => false //this case can be removed
case _ => false
}
答案 1 :(得分:1)
有一个原因是自动生成的equals方法没有做你建议的事情。 canEqual约定是一个常见且记录良好的约定,因为它允许处理更广泛的常见用例。
在实现equals方法时,您并不总是想要排除所有子类,只有那些覆盖equals的子类。如果如你所描述的那样,有一个Position3D类扩展了Position,而Position3D有一个额外的字段z,它是Position3D equals方法的一部分,那么当然Position3D不应该等于任何正常的2D位置。
但是,如果由于某种原因用户想要实例化一个匿名的Position类?
,该怎么办?val pos = new Position(1,2){}
或
trait Foo
val pos = new Position(1,2) with Foo
该职位应该被视为等于new Position(1,2)
。如果混合功能是微不足道的,或仅仅针对某些特定的本地范围用例,则在某些情况下不允许这种平等可能是单调乏味的。要像其他响应所建议的那样other.getClass == classOf[Position]
,要求混合特征或由于某种原因创建匿名类的客户端API可能会发现他们创建的位置甚至不等于他们自己。
考虑以下代码:
class Position(val x:Int, val y:Int) {
override def equals(other:Any) = other match {
case o:Position if o.getClass==classOf[Position] && o.x==x && o.y==y => true
case _ => false
}
}
trait Foo
val p1 = new Position(1,2) with Foo
val p2 = new Position(1,2) with Foo
p1 == p2
结果为false