переопределить метод equals () класса Pair - PullRequest
11 голосов
/ 25 сентября 2010

Этот вопрос ранее задавался в списке рассылки 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

Я думал, что постоянный (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

Может кто-нибудь объяснить, почему == становится правдой, но я не могу заставить их совпадать?

Ответы [ 4 ]

13 голосов
/ 25 сентября 2010

Проблема вашего примера в том, что вы переопределяете только метод equals анонимного класса, частью которого вы определяете свой конкретный кортеж. Давайте внимательнее посмотрим, что вы делаете, выполняя приведенный здесь код.

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

То, что здесь делает 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

И здесь вы можете точно увидеть, в чем проблема! Определение 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 реализует обобщения), Pair больше не параметризуется типами его составляющих. Следовательно, (Int, Int) имеет точно такой же тип, как и (String, String), и, следовательно, следующий код завершится ошибкой:

p == ("foo", "bar")

, поскольку Скала попытается разыграть (String, String) до (Int, Int).

Если вы хотите реализовать эту функцию, самое простое, что вы можете сделать, это использовать шаблон pimp my library, pimping a Pair. Однако вы не должны вызывать свой метод equals. Назовите это как-нибудь еще, например ~=. Я должен идти сейчас, однако, когда я вернусь, я мог бы дать вам код для этого. Это довольно легко. Вы должны посмотреть на реализацию equals в паре и удалить часть, которая сравнивает второй аргумент:)

3 голосов
/ 25 сентября 2010

Должен сказать, что Скала снова удивила меня. Еще несколько тестов показывают, что результат зависит от текущего контекста.

Если вы запустите:

object Pair{
    def main(args:Array[String]) {
        val T = new Pair(1, 2){
            override def equals(obj:Any)= obj.isInstanceOf[(Int, Int)] && obj.asInstanceOf[(Int, Int)]._1 == this._1
        }

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

}

Вы получаете: соответствует

И если вы запустите:

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

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

Вы получаете: не соответствует

Я всегда думал, что нет разницы между кодом в методах main () и кодом в теле объекта, что расширяет черту Application. Действительно странно.

2 голосов
/ 04 октября 2010

С разрешением # 3888 я могу дать окончательный ответ на этот вопрос.

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

    Нет, этоне имеет ничего общего с unapply.Как уже упоминалось: case (1,1) => - это 'Tuple Pattern', псевдоним 'Constructor Pattern' класса case Tuple2. Он соответствует только значению, созданному как Tuple2 (1, 1) или Pair (1, 1).1013 * Что действительно касается 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")
            }
        } 
    }
    

    Вы получаете «соответствие». Обратите внимание на () в кейсе

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

    в соответствии со спецификацией scala, раздел 8.1.5, это «шаблон стабильного идентификатора», case T соответствует любому значению v, так что T == v. Поэтому мы ДОЛЖНЫ получить «совпавший». "Несоответствующие" результаты просто вызваны ошибкой в ​​текущей реализации компилятора.

2 голосов
/ 27 сентября 2010

Кортежи имеют привилегии в шаблоне сопоставления.Это не ваши ежедневные занятия.Это в спецификации, раздел 8.1.7, «Шаблоны кортежей».

Когда вы говорите

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

Затем вызывается метод equals для (1, 1), который, конечно, говоритнет, спасибо, не равно.

Когда вы говорите

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

Тогда ваш метод равенства игнорируется из-за шаблона кортежа, и T._1 сравнивается с 1, а T._2 с 1и опять не совпадает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...