覆盖Pair的equals()方法

xie*_*fei 11 scala

先前在scala-user邮件列表中询问了此问题,但没有确认答案.

scala> val T = new Pair(1, 2){
override def equals(obj:Any) = obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1}
           }

T: (Int, Int) = (1,2)

scala> T match {
        case (1, 1) => println("matched")
        case _ => println("not matched")
   }

not matched


scala> (1, 1) match {
              case T => println("matched")
              case _ => println("not matched")
          }

not matched

scala> T == (1, 1)
res15: Boolean = true
Run Code Online (Sandbox Code Playgroud)

我认为常量(val)模式匹配结果取决于"等于"的返回值,但结果表明情况并非如此,那么标准是什么?

有人建议这case (1, 1) =>是一个提取器模式,Tuple2.unapply而是使用.所以我尝试了这些:

scala> Pair.unapply(T)
res1: Option[(Int, Int)] = Some((1,2))

scala> Pair.unapply(T).get == (1, 1)
res2: Boolean = true
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释为什么==会成真,但我不能让它们匹配?

Fla*_*gan 13

您的示例的问题是您只覆盖equals您定义特定元组的匿名类的方法.让我们仔细看看你在运行这里给出的代码时正在做什么.

val p = new Pair(1, 2) {
override def equals(obj:Any) = {
    obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1
  }
}
Run Code Online (Sandbox Code Playgroud)

Scala在这里做的是创建一个新的匿名类,它扩展Pair并覆盖它的等号.所以这相当于运行以下代码:

class Foo extends Pair(1,2) {
  override def equals(obj:Any) = {
      obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1
    }
}

val p = new Foo
Run Code Online (Sandbox Code Playgroud)

在这里,您可以确切地看到问题所在!定义equals不是对称的.p == (1,1)评估true(1,1) == p评估false!这是因为前者相当于p.equals((1,1))后者相当于(1,1).equals(p).在您给出的示例中,它不起作用,因为案例中的对象与匹配的对象进行比较,而不是相反.因此,正如您所指出的那样,Pair.unapply(p).get == (1, 1)评估为true,但是(1,1) == Pair.unapply(p).get评估为false,并且似乎后者是匹配时使用的.

但是,在任何情况下,创建一个非对称的等号是一个非常糟糕的主意,因为代码的执行取决于您比较对象的顺序.此外,您定义的等于还有一个问题 - 它失败并出现错误当您尝试比较p与任何Pair这是没有类型的(Int, Int).这是因为,在类型擦除(这是JVM如何实现泛型)之后,a Pair不再由其组成部分的类型进行参数化.因此,(Int, Int)具有完全相同的类型(String, String),因此,以下代码将失败并出现错误:

p == ("foo", "bar")
Run Code Online (Sandbox Code Playgroud)

因为Scala会尝试投射(String, String)(Int, Int).

如果你想实现这个功能,你可以做的最简单的事情是使用pimp我的库模式,拉皮条a Pair.但是,您不应该调用您的方法equals.称之为别的东西,比如~=.我现在必须要去,但是当我回来时,我可以给你代码.这很容易.你应该看equals一对中的实现并删除比较第二个参数的部分:)


xie*_*fei 2

通过#3888的决议,我可以对这个问题给出最终的答案。

  1. T match { case (1, 1) =>

    不,这与 无关unapply。正如临时提到的,case (1,1) =>是一个“元组模式”,是案例类 Tuple2 的“构造函数模式”的别名,它仅匹配构造为 Tuple2(1, 1) 或 Pair(1, 1) 的值。

    真正关心的unapply是“提取器模式”:

    object Pair {
        val T = new Pair(1,1){
            def unapply(p:(Int, Int)) :Boolean = this._1 == p._1
        }
        def main(args: Array[String]) = {
            (1, 2) match {
                case T() => println("matched")
                case _ => println("not matched")
            }
        } 
    }
    
    Run Code Online (Sandbox Code Playgroud)

    你就“匹配”了。注意()in the case 子句

  2. (1, 1) match { case T => ...

    根据 scala 规范第 8.1.5 节,这是一个“稳定标识符模式”,case T匹配任何值 v 使得 T == v。所以我们应该“匹配”。“不匹配”的结果只是由当前编译器实现中的错误引起的。