Подтвердить что ты не робот

Что такое * так * неправильно с наследованием класса case?

При поиске чего-то другого, совершенно из простого совпадения, я наткнулся на несколько комментариев о том, как наследуется дьявольское наследование класса случаев. Эта вещь называлась ProductN, негодяями и королями, эльфами и волшебниками и как теряется какое-то очень желательное свойство с наследованием классов случаев. Так что же случилось с наследованием класса case?

4b9b3361

Ответ 1

Одно слово: равенство

Классы

case имеют поставляемую реализацию equals и hashCode. Отношение эквивалентности, известное как equals, работает так (то есть должно иметь следующие свойства):

  • Для всех x; x equals x true (рефлексивный)
  • Для x, y, z; если x equals y и y equals z, то x equals z (переходный)
  • Для x, y; если x equals y, то y equals x (симметричный)

Как только вы разрешите равенство внутри иерархии наследования, вы можете разбить 2 и 3. Это тривиально продемонстрировано в следующем примере:

case class Point(x: Int, y: Int)
case class ColoredPoint(x: Int, y: Int, c: Color) extends Point(x, y) 

Тогда имеем:

Point(0, 0) equals ColoredPoint(0, 0, RED)

Но не

ColoredPoint(0, 0, RED) equals Point(0, 0)

Вы можете утверждать, что все иерархии классов могут иметь эту проблему, и это верно. Но классы случаев существуют специально для упрощения равенства с точки зрения разработчика (среди других причин), поэтому, если они ведут себя неинтуитивно, это будет определение собственной цели!


Были и другие причины; в частности, тот факт, что copy не работает должным образом и взаимодействие с шаблоном сопоставления.

Ответ 2

Это не так. И это хуже, чем ложь.

Как упоминалось aepurniet в любом случае, преемник класса, который сужает область определения, должен переопределять это равенство, поскольку сопоставление шаблонов должно работать точно так же, как и равенство (если попытаться сопоставить Point как ColoredPoint, тогда он не будет соответствовать, поскольку color не существует).

Это дает понимание того, как можно реализовать равенство иерархии классов case.

case class Point(x: Int, y: Int)
case class ColoredPoint(x: Int, y: Int, c: Color) extends Point(x, y)

Point(0, 0) equals ColoredPoint(0, 0, RED)  // false
Point(0, 0) equals ColoredPoint(0, 0, null) // true

ColoredPoint(0, 0, RED) equals Point(0, 0)  // false
ColoredPoint(0, 0, null) equals Point(0, 0) // true

В конечном итоге можно удовлетворить требования равенства равенства даже для преемника класса case (без переопределения равенства).

case class ColoredPoint(x: Int, y: Int, c: String)
class RedPoint(x: Int, y: Int) extends ColoredPoint(x, y, "red")
class GreenPoint(x: Int, y: Int) extends ColoredPoint(x, y, "green")

val colored = ColoredPoint(0, 0, "red")
val red1 = new RedPoint(0, 0)
val red2 = new RedPoint(0, 0)
val green = new GreenPoint(0, 0)

red1 equals colored // true
red2 equals colored // true
red1 equals red2 // true

colored equals green // false
red1 equals green // false
red2 equals green // false

def foo(p: GreenPoint) = ???