66 votes

Qu'est-ce qui est *si* mauvais dans l'héritage des classes de cas ?

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 ?

109voto

oxbow_lakes Points 70013

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) :

  1. Pour tous les x ; x equals x es true (réflexif)
  2. Para x , y , z ; si x equals y y y equals z puis x equals z (transitif)
  3. Para x , y ; si x equals y puis y 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 .

-2voto

user1303559 Points 390

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) = ???

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X