Alors que je cherchais autre chose, par pure coïncidence, je suis tombé sur quelques commentaires sur le caractère diabolique de l'héritage des classes de cas. Il y avait cette chose appelée ProductN
Les classes de cas, les misérables et les rois, les elfes et les magiciens et la façon dont une sorte de propriété très souhaitable est perdue avec l'héritage des classes de cas. Alors qu'est-ce qui ne va pas avec l'héritage des classes de cas ?
Réponses
Trop de publicités?Un seul mot : égalité
case
Les classes sont livrées avec une implémentation fournie de equals
y hashCode
. La relation d'équivalence, connue sous le nom de equals
fonctionne comme suit (c'est-à-dire qu'il doit avoir les propriétés suivantes) :
- Pour tous les
x
;x equals x
estrue
(réflexif) - Para
x
,y
,z
; six equals y
yy equals z
puisx equals z
(transitif) - Para
x
,y
; six equals y
puisy equals x
(symétrique)
Dès que vous permettez l'égalité au sein d'une hiérarchie d'héritage, vous pouvez rompre 2 et 3. Ceci est trivialement démontré par l'exemple suivant :
case class Point(x: Int, y: Int)
case class ColoredPoint(x: Int, y: Int, c: Color) extends Point(x, y)
Alors nous avons :
Point(0, 0) equals ColoredPoint(0, 0, RED)
Mais no
ColoredPoint(0, 0, RED) equals Point(0, 0)
Vous pourriez dire que toutes les hiérarchies de classes peuvent avoir ce problème, et c'est vrai. Mais les classes de cas existent spécifiquement pour simplifier l'égalité du point de vue du développeur (entre autres raisons), donc les faire se comporter de la manière suivante de manière non-intuitive serait la définition d'un but contre son camp !
Il y avait aussi d'autres raisons, notamment le fait que copy
ne fonctionne pas comme prévu y interaction avec le comparateur de motifs .
Ce n'est pas tout à fait vrai. Et c'est pire qu'un mensonge.
Comme mentionné par aepurniet dans tous les cas, le successeur de classe qui restreint une zone de définition doit redéfinir l'égalité parce que le filtrage de motifs doit fonctionner exactement comme l'égalité (si on essaie de faire correspondre Point
como ColoredPoint
alors il n'y aura pas de correspondance puisque color
n'existe pas).
Cela permet de comprendre comment l'égalité de la hiérarchie des classes de cas peut être mise en œuvre.
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
Finalement, il est possible de satisfaire aux exigences de la relation d'égalité même pour le successeur de la classe de cas (sans remplacer l'égalité).
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) = ???