Zamiennik dla instanceof Java?

10

Jestem więc całkiem nowy w programowaniu w świecie rzeczywistym (poza projektami akademickimi) i natrafiłem na wiele postów, które mówią, że używanie instanceofjest złe, aby określić, jaką klasą jest dany obiekt.

Moja sytuacja jest taka, że ​​mam trzy klasy, podstawową klasę produktu, jedną, która ją rozszerza, i drugą, która ją rozszerza. Wszystkie są przechowywane w tej samej tabeli w bazie danych i mam trochę kodu, który wymaga użycia metod na każdym z nich, aby pobrać dane.

Jaka jest najlepsza praktyka, aby obejść ten sposób? Przeczytałem kilka rzeczy o polimorfizmie, ale nie mogę znaleźć żadnych przykładów, które rozwiązałyby mój problem. Wszystkie zwykle zastępują metodę, która dla mnie nie działa, ponieważ muszę wyciągać różne rzeczy z różnych obiektów.

Czy istnieje lepszy sposób, aby to zrobić, czy utknąłem przy użyciu instanceoflub jakiegoś odbicia, aby uzyskać pola specyficzne dla obiektów?

Thomas Mitchell
źródło
10
Nie chodzi o to, że słowo kluczowe instanceofjest nieprawidłowe; Problemem jest zazwyczaj próba znalezienia klasy obiektu. Nie zawsze źle, ale prawdopodobnie tak jest w twoim przypadku. Może jeśli powiesz nam, co próbujesz osiągnąć, możemy zaproponować rozwiązanie wykorzystujące polimorfizm.
Andres F.,
2
Okej, każda klasa ma specyficzne dla niej dane. Chcę wyciągnąć z nich określone dane. Wydaje mi się, że udało mi się uzyskać odpowiedź z pomocą. Coś w rodzaju wywoływanej metody, getSpecifics()która jest implementowana w różny sposób dla każdej z nich, przy czym każda zwraca dane specyficzne dla klasy?
Thomas Mitchell,

Odpowiedzi:

16

Powód instanceofjest odradzany, ponieważ nie jest to OOP.

Osoba wywołująca / użytkownik obiektu nie powinna mieć powodu, aby wiedzieć, która konkretna klasa jest instancją, poza którą typem zmiennej jest zadeklarowana.

Jeśli potrzebujesz różnych zachowań w podklasach, dodaj metodę i zaimplementuj je inaczej.

maniak zapadkowy
źródło
1
Tak więc metoda getSpecifics()(lub coś podobnego) na każdej klasie, która zwróci specyfikę każdej klasy. Czy to najlepsze podejście?
Thomas Mitchell,
@ Schmooo Naprawdę musisz pomyśleć o tym, gdzie istnieje wspólna funkcjonalność w drzewie klas i jakie kontrakty istnieją na każdym poziomie.
3
Ale co z przypadkiem, gdy obiekt przecina warstwy, a informacje o typie są tracone? Przykład Tworzę List. Przekazuję go do obiektu, który bierze dowolny Iterable. Teraz ten drugi obiekt przekazuje go do trzeciego obiektu, który zajmuje albo Listoptymalizację, albo Iterableale jest znacznie wolniejszy. Drugi nie powinien wiedzieć, że to lista, ale trzeci bardzo chciałby wiedzieć. Czy trzeci obiekt nie powinien sprawdzać, czy instancja może zastosować optymalizację? Zobacz na przykład guawa FluentIterable, która właśnie to robi.
Laurent Bourgault-Roy
1
Powiedziałbym, że nie zawsze trzeba dodawać metodę do klasy. Na przykład jakiś kod otrzymuje wyjątek i powinien coś z nim zrobić w zależności od jego typu. Powiedz, robi raport lub robi coś, aby naprawić błąd. Ten rodzaj funkcjonalności zdecydowanie nie pasuje do wyjątków. Lub, jeśli klasy pochodzą z jakiejś zewnętrznej biblioteki, to nie masz nawet możliwości zmodyfikowania tych klas. W tym przypadku uważam, że absolutnie uzasadnione jest poleganie na instanceof.
Malcolm,
9

instanceof niekoniecznie jest złą rzeczą, jednak należy na to spojrzeć.

Przykładem prawidłowego działania jest miejsce, w którym można uzyskać kolekcję typu podstawowego, a chcesz tylko te z podtypem. Odzyskiwanie adresów sieciowych z NetworkInterface.getNetworkInterfaces()powrotem zwraca obiekty NetworkInterface, które mają kolekcję obiektów InetAddress, niektóre z nich to Inet4Address, a niektóre Inet6Address. Jeśli chcemy filtrować kolekcję pod kątem obiektów Inet4Address, konieczne jest użycie instanceof.

W sytuacji opisanej w oryginalnym poście istnieje klasa podstawowa, coś, co rozszerza tę klasę podstawową i coś, co rozszerza klasę rozszerzoną. Chociaż nie jest to w pełni pouczające, wydaje się, że ma to podstawy niektórych mniej niż idealnych projektów.

Gdy zwracasz klasę podstawową, chyba że istnieje uzasadniony powód, aby tak ją zaprojektować (zgodność wsteczna między specyfikacjami wcześniejszej wersji), nie powinieneś próbować zaglądać do typów bazowych. Jeśli otrzymasz zestaw, wiesz, że otrzymujesz zestaw. Pozwala to deweloperowi później zmienić zdanie, aby zwrócić bardziej konkretny typ (SortedSet) lub zmienić typ bazowy (HashSet na TreeSet) bez zerwania czegokolwiek.

Ponownie przeanalizuj projekt struktury obiektów i ich rodzicielstwa, aby zobaczyć, czy można stworzyć lepszy model klasy, który nie wymaga rozróżnienia typów.


źródło
Możliwe, że określenie przez interfejs isOfType(SomeEnum.IPv4)metody może być lepszym sposobem na filtrowanie takich cech niż sprawdzanie konkretnego typu za pomocą instanceof. Co jeśli chcesz później podzielić klasę implementacji IPv4? Nie zawsze tak jest lepiej, ale to rozważanie.
Steven Schlansker
@StevenSchlansker To może działać dla tej konkretnej klasy. Ale co by było, gdyby trzeba było sprawdzić konkretny typ, aby sprawdzić, czy rzut jest możliwy (lub czy po prostu rzuciłby wyjątek?) Inet6Address implementuje więcej metod niż InetAddress. Trudno byłoby mu pracować z interfejsami (wyliczenie, które wylicza każdą klasę w pakiecie? Lub moduł ładujący klasy?), A to oznaczałoby, że metoda musiałaby zostać wdrożona ręcznie (lub jakiś kod refleksyjny w Object). Zastanów się, jak to zrobić (o instanceof Serializable) || (o instanceof Externalizable). instanceof jest lepszy niż alternatywa
1

Możesz użyć metody getClass ().

Czy na pewno potrzebujesz trzech różnych klas? Może jedna klasa z przełącznikiem w środku będzie służyć lepiej?

Gangnus
źródło
7
getClassi instanceofdziel się wadami. Polimorfizm jest lepszy niż oba, gdy pasuje, i nie widzę, aby nie pasował do przypadku użycia OP.
Nie mam nic przeciwko twojemu pierwszemu zdaniu. Ale drugi - przepraszam, nie rozumiem, co masz na myśli.
Gangnus,
1
Ze getClassmam nadal nie dzielić ten sam problem, jak przy użyciu instanceof? Nadal będę musiał dowiedzieć się, który mam, a następnie wywołać zestaw funkcji. Idealnie chciałbym metody, która zwraca dane specyficzne dla tej klasy bez konieczności rzutowania na ten obiekt.
Thomas Mitchell,
Narzekam, że ignorujesz polimorfizm, ponieważ często jest to lepszy wybór. Jestem prawie pewien, że przypadek użycia w pytaniu można wykonać z polimorfizmem, więc nie widzę potrzeby używania żadnego z nich. (Poza tym samo odtworzenie odpowiedzi innej osoby nie jest przyjemne.)
2
@ Gangus Nie uważam tego za „atak”, to znaczy bez obrazy. Ale cokolwiek. Nie, pytanie nie brzmi „co jeszcze można użyć”, ale „czy można to zrobić lepiej”. O ile nie chcesz się kłócić, getClassjest to lepszy sposób, nie ma biznesu, który zajmuje większość odpowiedzi ;-)
1

Zwykle, gdy chcę poznać rodzaj czegoś, oznacza to, że źle zaimplementowałem strukturę obiektu. W większości przypadków sprowadza się to do naruszenia LSP .

Są jednak chwile, w których chciałbym mieć sposób na dynamiczną wysyłkę i zaoszczędzić mnóstwo kodu płyty kotłowej i zabezpieczyć moją strukturę obiektu na przyszłość. C # zapewnia dynamiczne słowo kluczowe w nowszych wersjach frameworka, ale o ile wiem, Java nadal nie ma czegoś podobnego.

To powiedziawszy, instanceof jest na ogół lepszy niż porównywanie klas, ponieważ odpowiednio obsługuje dziedziczenie. Możesz także użyć metod takich jak isAssignableFrom i inne z interfejsu API reflexion. Jeśli chcesz zaimplementować coś w rodzaju dynamicznej wysyłki, można to zrobić za pomocą interfejsu API odbicia, ale uwaga: będzie ona wolna. Używaj ostrożnie, najlepiej jeśli to możliwe, napraw strukturę obiektu i desing aplikacji.

Mam nadzieję że to pomoże

Newtopian
źródło