2012-06-22 2 views
56

Mentre cercavo qualcos'altro, per pura coincidenza mi sono imbattuto in pochi commenti su quanto diabolica sia l'ereditarietà della case case. C'era questa cosa chiamata ProductN, disgrazia e re, elfi e maghi e come si perde una specie di proprietà molto desiderabile con l'ereditarietà delle classi di casi. Quindi cosa c'è di così sbagliato nell'ereditarietà della case case?Che cosa è * così * sbagliato nell'ereditarietà della case case?

risposta

97

Una sola parola: uguaglianza

case classi sono dotati di un'implementazione fornito di equals e hashCode. La relazione di equivalenza, noto come equals così (cioè deve avere le seguenti proprietà):

  1. Per tutti x; x equals x è true (riflessivo)
  2. per x, y, z; se x equals y e y equals z quindi x equals z (transitiva)
  3. per x, ; se poi x equals yy equals x (simmetrica)

Non appena si consente per l'uguaglianza all'interno di una gerarchia di ereditarietà si può rompere 2 e 3. Questo è banalmente dimostrato dal seguente esempio:

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

Poi abbiamo :

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

Ma non

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

Si potrebbe sostenere che tutte le gerarchie di classi possono avere questo problema, e questo è vero. Ma le classi di casi esistono specificamente per semplificare l'uguaglianza dal punto di vista dello sviluppatore (tra le altre ragioni), quindi avere loro il comportamento non intuitivamente sarebbe la definizione di un proprio obiettivo!


Ci sono stati anche altri motivi; in particolare il fatto che copy did not work as expected e interaction with the pattern matcher.

+0

E che dire di una piccola elaborazione :)? –

+2

Sembra che un'equivalenza asimmetrica sarebbe una cosa utile nel paradigma OO, nello stesso modo in cui a livello di tipo un 'ColoredPoint' è-a 'Punto' ma non viceversa. Potrebbe chiamarlo qualcosa di diverso da "uguali" anche se ... forse "subequidi"? –

+0

@LuigiPlinge forse 'canReplace',' sostituisce', 'specifica' o' sovrascrive 'per la relazione inversa? Qualsiasi cosa indichi '> =' -ness (o '>:' se ti piace) di esso. Mi sembra molto più facile nominarlo in termini di '> =' piuttosto che '<='. –

-2

Questo non è nel complesso vero. E questo è peggio della menzogna.

Come menzionato da aepurniet in qualsiasi classe successore caso che restringe un'area definizione deve ridefinire l'uguaglianza, poiché pattern matching deve funzionare esattamente come uguaglianza (se cercare di abbinare Point come ColoredPoint allora non sarà abbinato poiché color non è disponibile).

Questo consente di comprendere come implementare l'uguaglianza della gerarchia delle classi di casi.

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 è possibile soddisfare esigenze del rapporto uguaglianza anche per il caso di classe successore (senza sovrascrivere della parità).

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