scala - relaciones - ¿Qué está*tan*mal con la herencia de clase de caso?
tipos de herencia en java (2)
Eso no es cierto en general. Y esto es peor que la mentira.
Como mencionó aepurniet en cualquier caso, el sucesor de clase que constriñe un área de definición debe redefinir la igualdad porque la coincidencia de patrones debe funcionar exactamente como igualdad (si intenta hacer coincidir el Point
como ColoredPoint
, no coincidirá ya que el color
no existe).
Eso da una idea de cómo podría implementarse la igualdad de la jerarquía de clases de casos.
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
Eventualmente, es posible satisfacer los requisitos de la relación de igualdad incluso para el sucesor de la clase de caso (sin anulación de la igualdad).
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) = ???
Mientras buscaba algo más, por mera coincidencia, tropecé con algunos comentarios sobre cómo es la herencia de la clase de caso diabólico. Había algo llamado ProductN
, miserables y reyes, elfos y magos, y cómo se pierde algún tipo de propiedad muy deseable con la herencia de las clases de casos. Entonces, ¿qué tiene de malo la herencia de la clase de caso?
Una palabra: igualdad
case
clases de case
vienen con una implementación provista de equals
y hashCode
. La relación de equivalencia, conocida como equals
funciona de esta manera (es decir, debe tener las siguientes propiedades):
- Para todo
x
;x equals x
estrue
(reflexivo) - Para
x
,y
,z
; six equals y
yy equals z
entoncesx equals z
(transitivo) - Para
x
,y
; six equals y
entoncesy equals x
(simétrica)
Tan pronto como permita la igualdad dentro de una jerarquía de herencia, puede dividir 2 y 3. Esto se demuestra trivialmente con el siguiente ejemplo:
case class Point(x: Int, y: Int)
case class ColoredPoint(x: Int, y: Int, c: Color) extends Point(x, y)
Entonces nosotros tenemos:
Point(0, 0) equals ColoredPoint(0, 0, RED)
Pero no
ColoredPoint(0, 0, RED) equals Point(0, 0)
Podría argumentar que todas las jerarquías de clase pueden tener este problema, y esto es cierto. Pero las clases de casos existen específicamente para simplificar la igualdad desde la perspectiva de un desarrollador (entre otras razones), por lo que hacer que se comporten de forma no intuitiva sería la definición de un objetivo propio.
También había otras razones; especialmente el hecho de que la copy
no funcionó como se esperaba y la interacción con el patrón de coincidencia .