Mam taką trochę męczącą potrzebę zaimplementowania metod compareTo
oraz equal
dla obiektów różnych klas implementujących wspólny interfejs Comparable<Base>
. Obie metody muszą oczywiście spełniać wszystkie warunki dla poprawnie napisanych metod equal
i compare
(np. przechodniość, zwrotność itd.) oraz muszą być ze sobą spójne (zachowane równości i nierówności). Metoda compareTo
(inaczej niż zwykle) nie może nigdy rzucić wyjątkiem ClassCastException
- nawet jeżeli porównane muszą by dwa obiekty, które mają wspólną bazę, ale same pochodzą z różnych gałęzi dziedziczenia. W ostateczności najwyższą wspólną bazą jest klasa Base i wtedy obiekty takie muszą być porównane za pomocą jej porządku (lub pierwszej wcześniejszej wspólnej bazy). Gdyby zdarzyło się porównywać z nullem, to zamiast rzucania wyjątkiem każdy null musi być z definicji największy, więc sortowany na końcu przy porządku rosnącym (ale to szczegół).
Trudność polega na tym, że tylko obiekt klasy potomnej wie jak porównać siebie z obiektem swojej klasy i nadrzędnych. Oznacza to, że obiekt nadklasy musi delegować porównania i równości do metod equals
i compareTo
obiektu klasy potomnej oraz wywołać jej metodę compareTo
(lub podobną) z pierwszej znalezionej wspólnej klasy bazowej dla obiektów z różnych drzew dziedziczenia. Poszczególne klasy mogą porównywać po swoich polach, wykorzystywać porównania z klas nadrzędnych lub nawet trywialnie odwoływać się do porównania z nadklasy (kiedy żadne nowe właściwości zmieniające porównanie/równość nie doszły), więc przy okazji trzeba systemowo ominąć rekurencję gdy nadklasa odwoła się z porównaniem do podklasy, a ta do porównania odwoła się do swojej nadklasy, czyli z powrotem do tej samej samej klasy.
Jestem niemal pewien, że był na to jakiś wzorzec, ale jego nazwa kompletnie wyleciała mi z głowy (o ile był), więc kombinuję właśnie nad implementacją tego. Byłoby miło gdyby ktoś przypomniał mi jego nazwę (patterna, wtedy wygoogluję) lub ewentualnie zaproponował szablon metod compareTo
i equals
, który spełniałyby powyższe warunki.
ps. Byłoby dobrze, żeby ominąć refleksję przy wywoływaniu porównywania właściwego dla klasy nadrzędnej (ale nie wiem czy to możliwe inaczej niż przez serię else-if z isAssignableFrom
i getSuperClass
).
ps2. Każda klasa z łańcucha jest niezmienna (nie ma mutatorów) za wyjątkiem faktu dziedziczenia i konieczności użycia metod wirtualnych.