2012-06-22 23 views
56

Szukając czegoś innego, zupełnie przypadkowo natknąłem się na kilka komentarzy na temat tego, jak diabelskie dziedzictwo klas przypadków jest. Było to coś o nazwie ProductN, nędzników i królów, elfów i czarodziejów oraz jak traci się jakąś bardzo pożądaną własność z dziedziczeniem klas spraw. Więc co jest nie tak z dziedziczeniem klasy przypadku?Co to jest * tak * nie tak z dziedziczeniem klasy spraw?

Odpowiedz

97

Jednym słowem: równość

case klasy pochodzą z dostarczonym realizacji equals i hashCode. Stosunek równoważności, znany jako equals działa w następujący sposób (to znaczy musi posiadać następujące właściwości)

  1. Dla wszystkich x; x equals x jest true (refleksyjny)
  2. dla x, y, z; jeśli x equals y i y equals z następnie (przechodnie)
  3. Dla x, y; jeśli x equals y następnie y equals x (symetrycznym)

Tak szybko, jak pozwalają na równość w hierarchii dziedziczenia można złamać 2 i 3. to jest trywialnie świadczy następujący przykład:

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

Następnie mamy :

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

Ale nie

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

Można argumentować, że wszystkie hierarchie klas mogą mieć ten problem, i to prawda. Ale klasy przypadków istnieją specjalnie po to, aby uprościć równość z perspektywy programisty (między innymi), więc nieintuicyjnie zachowując je, zachowamy się tak, jakby to było .


Były też inne powody; w szczególności fakt, że copy did not work as expected i interaction with the pattern matcher.

+0

A co z małym opracowaniem :)? –

+2

Wygląda na to, że taka asymetryczna równoważność byłaby użyteczna w paradygmacie OO, w taki sam sposób, że na poziomie typu 'ColoredPoint' jest - Point, ale nie na odwrót. Może nazwać to czymś innym niż "równa się" ... może "subEquals"? –

+0

@LuigiPlinge być może 'canReplace',' zastępuje', 'określa' lub 'przesłonia' dla odwrotnej relacji? Wszystko dla wskazania '> =' -ness (lub '>:' jeśli chcesz) z tego. Wydaje mi się, że łatwiej mi to nazwać w kategoriach '> =' niż '<='. –

-2

To nie jest całkowita prawda. A to jest gorsze niż kłamstwo.

Jak wspomniano przez aepurniet w każdym przypadku klasy następcy, który zwęża obszar definicja musi przedefiniować równości, ponieważ dopasowanie wzorca musi działać dokładnie jak równość (jeśli starają się dopasować Point jak ColoredPoint to nie będzie dopasowana od color nie istnieje).

Dają one zrozumienie, w jaki sposób można wdrożyć równość hierarchii klas przypadków.

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 

Ostatecznie jest to możliwe, aby zaspokoić wymagania względem równości nawet dla klasy sprawa następcy (bez przesłanianie równości).

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