Przeczytałem ten artykuł: Jak napisać metodę równości w Javie .
Zasadniczo zapewnia rozwiązanie dla metody equals (), która obsługuje dziedziczenie:
Point2D twoD = new Point2D(10, 20);
Point3D threeD = new Point3D(10, 20, 50);
twoD.equals(threeD); // true
threeD.equals(twoD); // true
Ale czy to dobry pomysł? te dwa wystąpienia wydają się być równe, ale mogą mieć dwa różne kody skrótu. Czy to nie jest trochę złe?
Wierzę, że lepiej by to osiągnąć, zamiast tego rzucając operandami.
java
c#
scala
comparison
Wes
źródło
źródło
z
współrzędnej zerowej może być przydatną konwencją dla niektórych aplikacji (przychodzą na myśl wczesne systemy CAD obsługujące starsze dane). Ale to konwencja arbitralna. Płaszczyzny w przestrzeniach o 3 lub więcej wymiarach mogą mieć dowolną orientację ... to sprawia, że interesujące problemy są interesujące.Odpowiedzi:
Nie powinna to być równość, ponieważ psuje przechodniość . Rozważ te dwa wyrażenia:
Ponieważ równość jest przechodnia, powinno to oznaczać, że prawdziwe jest również następujące wyrażenie:
Ale oczywiście - nie jest.
Twój pomysł na rzutowanie jest poprawny - spodziewaj się, że w Javie rzutowanie oznacza po prostu rzutowanie typu odwołania. To, czego naprawdę chcesz, to metoda konwersji, która utworzy nowy
Point2D
obiekt zPoint3D
obiektu. To sprawiłoby, że wyrażenie to byłoby bardziej znaczące:źródło
(10, 20, 50)
równa się(10, 20, 60)
jest w porządku. Dbamy tylko o10
i20
.Point2D
istniećprojectXYZ()
metoda zapewnieniaPoint3D
reprezentacji samego siebie? Innymi słowy, czy wdrożenia powinny się znać?Point2D
wydaje się prostsze, ponieważ rzutowanie punktów 2D wymaga najpierw zdefiniowania ich płaszczyzny w przestrzeni 3D. Jeśli punkt 2D wie, że jest to płaszczyzna, to jest to już punkt 3D. Jeśli nie, to nie może wyświetlić. Przypomina mi się Flatland Abbotta .Plane3D
obiekt, który zdefiniuje płaszczyznę w przestrzeni 3D, ta płaszczyzna może miećlift
metodę (2D-> 3D podnosi, nie rzutuje), która zaakceptuje aPoint2D
i liczbę dla „trzeciej osi „- odległość od płaszczyzny wzdłuż płaszczyzny normalnej. Aby ułatwić korzystanie, możesz zdefiniować wspólne płaszczyzny jako stałe statyczne, abyś mógł robić rzeczy takie jakPlane3D.XY.lift(new Point2D(10, 20), 50).equals(new Point3D(10, 20, 50))
Odchodzę od czytania artykułu, myśląc o mądrości Alana J. Perlisa:
Fakt, że poprawność „równości” jest rodzajem problemu, który utrzymuje Martina Orderky'ego, wynalazcę Scali w nocy, powinien przerwać, czy przesłonięcie
equals
drzewa spadkowego jest dobrym pomysłem.To, co się dzieje, gdy nie mamy szczęścia, to
ColoredPoint
to, że nasza geometria zawodzi, ponieważ użyliśmy dziedziczenia do rozprzestrzeniania typów danych, zamiast tworzyć jeden dobry. Dzieje się tak pomimo konieczności cofnięcia się i zmodyfikowania węzła głównego drzewa dziedziczenia, abyequals
działał. Dlaczego nie dodać po prostuz
aicolor
doPoint
?Dobry powód by to działał
Point
iColoredPoint
działał w różnych domenach ... przynajmniej jeśli te domeny nigdy się nie zmieszały. Jednak w takim przypadku nie musimy nadpisywaćequals
. PorównywanieColoredPoint
iPoint
dla równości ma sens tylko w trzeciej domenie, w której mogą się mieszać. I w takim przypadku prawdopodobnie lepiej jest, aby „równość” była dostosowana do tej trzeciej domeny, niż próbować zastosować semantykę równości z jednej lub drugiej lub obu niezmienionych domen. Innymi słowy, „równość” powinna być zdefiniowana lokalnie w miejscu, w którym płynie błoto z obu stron, ponieważ możemy nie chciećColoredPoint.equals(pt)
zawieść przeciwko przypadkom,Point
nawet jeśli autorColoredPoint
uważał, że to dobry pomysł sześć miesięcy temu o 2 nad ranem .źródło
Kiedy dawni bogowie programowania wymyślali programowanie obiektowe za pomocą klas, zdecydowali, kiedy chodzi o kompozycję i dziedziczenie, aby mieć dwa relacje dla obiektu: „jest” i „ma”.
To częściowo rozwiązało problem, że podklasy różnią się od klas nadrzędnych, ale uczyniły je użytecznymi bez łamania kodu. Ponieważ instancja podklasy „jest” obiektem nadklasy i można go bezpośrednio zastąpić, nawet jeśli podklasa ma więcej funkcji składowych lub elementów danych, „ma” gwarantuje, że wykona wszystkie funkcje elementu nadrzędnego i będzie mieć wszystkie członkowie. Można więc powiedzieć, że Point3D ”to„ Point, a Point2D ”to„ Point, jeśli oba dziedziczą po Point. Dodatkowo Point3D może być podklasą Point2D.
Równość między klasami jest jednak specyficzna dla dziedziny, a powyższy przykład jest dwuznaczny co do tego, czego programista potrzebuje, aby program działał poprawnie. Zasadniczo przestrzegane są reguły domeny matematycznej, a wartości danych generowałyby równość, jeśli ograniczysz zakres porównania tylko w tym przypadku do dwóch wymiarów, ale nie w przypadku porównania wszystkich elementów danych.
Otrzymujesz więc tabelę zawężających się równości:
Zazwyczaj wybierasz najsurowsze reguły, które możesz nadal wykonywać wszystkie niezbędne funkcje w domenie problemowej. Wbudowane testy równości dla liczb są tak restrykcyjne, jak mogą być do celów matematycznych, ale programista ma wiele sposobów na to, jeśli to nie jest cel, w tym zaokrąglanie w górę / w dół, obcinanie, gt, lt itp. . Obiekty ze znacznikami czasu są często porównywane według czasu ich wygenerowania, dlatego każde wystąpienie musi być unikalne, aby porównania były bardzo szczegółowe.
Czynnikiem projektowym w tym przypadku jest określenie skutecznych sposobów porównywania obiektów. Czasami musisz wykonać rekurencyjne porównanie wszystkich elementów danych z elementami, co może być bardzo kosztowne, jeśli masz wiele obiektów z dużą liczbą elementów danych. Alternatywą jest porównywanie tylko odpowiednich wartości danych lub sprawienie, by obiekt wygenerował wartość skrótu danych elementów, których to dotyczy, w celu szybkiego porównania z innymi podobnymi obiektami, utrzymywania kolekcji posortowanych i przycinanych, aby porównania były szybsze i mniej obciążające procesorem, a być może zezwalać obiektom, które są identyczne pod względem danych, które mają zostać ubite, a na ich miejsce należy umieścić zduplikowany wskaźnik do pojedynczego obiektu.
źródło
Zasada jest taka, że za każdym razem, gdy nadpisujesz
hashcode()
, nadpisujeszequals()
i vice versa. To, czy jest to dobry pomysł, czy nie, zależy od zamierzonego zastosowania. Osobiście wybrałbym inną metodę (isLike()
lub podobną), aby osiągnąć ten sam efekt.źródło
Często przydatne jest, aby klasy niepubliczne miały metodę testowania równoważności, która pozwala obiektom różnych typów uważać się za „równe”, jeśli reprezentują te same informacje, ale ponieważ Java nie pozwala, w jaki sposób klasy mogą się podszywać pod siebie inne często dobrze jest mieć jeden typ opakowania publicznego, w przypadkach, w których możliwe jest posiadanie równoważnych obiektów o różnych reprezentacjach.
Rozważmy na przykład klasę enkapsulującą niezmienną macierz 2D
double
wartości. Jeśli jedna metoda zewnętrzna prosi o matrycę tożsamości o wielkości 1000, druga prosi o matrycę diagonalną i przekazuje tablicę zawierającą 1000, a trzecia pyta o matrycę 2D i przekazuje macierz 1000 x 1000, w której wszystkie elementy na pierwotnej przekątnej wynoszą 1,0 a wszystkie inne są zerowe, obiekty podane wszystkim trzem klasom mogą wewnętrznie korzystać z różnych magazynów zaplecza [pierwszy z pojedynczym polem wielkości, drugi z tablicą tysiąca elementów, a trzeci z tysiącem tablic 1000 elementów], ale powinny zgłaszać się nawzajem jako równoważne [ponieważ wszystkie trzy enkapsulują niezmienną macierz 1000 x 1000 z jednymi na przekątnej i zerami wszędzie indziej].Poza faktem, że ukrywa istnienie odrębnych typów zaplecza sklepowego, opakowanie jest również przydatne do ułatwienia porównań, ponieważ sprawdzanie równoważności elementów będzie zasadniczo procesem wieloetapowym. Zapytaj pierwszy element, czy wie, czy jest równy drugiemu; jeśli nie wie, zapytaj drugiego, czy wie, czy jest równy pierwszemu. Jeśli żaden obiekt nie wie, zapytaj każdą tablicę o zawartość jej poszczególnych elementów [można dodać inne kontrole przed podjęciem decyzji o długim powolnym porównywaniu poszczególnych elementów].
Zauważ, że metoda testu równoważności dla każdego obiektu w tym scenariuszu musiałaby zwrócić wartość trójstanową („Tak, jestem równoważny”, „Nie, nie jestem równoważny” lub „Nie wiem”), więc normalna metoda „równa się” nie byłaby odpowiednia. Podczas gdy jakikolwiek obiekt może po prostu odpowiedzieć „nie wiem”, gdy zostanie o nie zapytany, dodanie logiki do np. Macierzy diagonalnej, która nie zawracałaby sobie głowy pytaniem o dowolną matrycę tożsamości lub macierz diagonalną o dowolne elementy poza główną przekątną, znacznie przyspieszyłoby porównania między takimi typy.
źródło