Java Slayer – Equals Slayer – rozwiązanie

Paweł był blisko, a i ja tworzyłem to zadanie z myślą panu J Blochu. Problem z kodem jest jednak troszkę inny. Pisząc metodę equals musi ona spełniać kilka podstawowych zasad. Jedną z nich jest symetryczność, czyli a.equals(b) == b.equals(a).
Przy dziedziczeniu oznacza to, że dobrze by było żeby jeżeli b rozszerza a to działanie equals w b uwzględnia ten fakt. Dodatkowo mamy tu zasadę Liskov, która mówi, że pod klasa powinna zachowywać się jak nad klasa. Czyli b powinno być w stanie porównać się z a tak jakby było a.
Ok w czym problem. Otóż nigdy nie będzie wywołane tak naprawdę porównanie ColorPoint. ColorPoint jest instancją Point zatem niezależnie jaki obiekt (ColorPoint, czy Point) będziemy porównywać to zostanie zwrócony wynik z linii 79.
Prawidłowa implementacja powinna wyglądać w następujący sposób:

Listing 1. Prawidłowa implementacja metody ColorPoint.equals

@Override
public boolean equals(Object obj) {
	if (this == obj)
		return true;
	if (!(obj instanceof Point))
		return false;
	if (obj instanceof ColorPoint){
		ColorPoint p = (ColorPoint) obj;
		return super.equals(obj) && this.color.equals(p.color);
	}
	if (obj instanceof Point)
		return super.equals(obj);
	return false;
}

W tym przypadku najpierw sprawdzimy czy obiekt należy do bardziej szczegółowej klasy, a dopiero potem czy jest bardziej ogólny. Dobrze napisana metoda equals powinna tego typu sprawdzenia wykonywać od liścia do korzenia.

Jedna myśl na temat “Java Slayer – Equals Slayer – rozwiązanie

  1. Oj Koziołku, w ten sposób nadal możesz złamać symetryczność metody equals (czyli jej kontrakt).

    Wyobraź sobie, że teraz napiszę klasę dziedziczącą z ColorPoint, np. tak:
    public class TimeColorPoint extends ColorPoint {
    private Date creationDate = new Date();
    //Pomijamy to co zbędne

    public boolean equals(Object obj) {
    //pomijamy sprawdzanie nulla
    if (obj.getClass() != this.getClass()) {
    return false;
    }

    //sprawdzamy super.equals i dodane pole „creationDate”.
    }

    Teraz możemy zrobić sobie taki TimeColorPoint i taki ColorPoint, że colorPoint.equals(timePoint) daje TRUE, a timePoint.equals(colorPoint) daje FALSE.

    Upieram się przy porównywaniu klas, a nie używaniu „instanceof” 🙂

    Pozdrawiam

Napisz odpowiedź

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax