2012-01-21 13 views
11

Jak zawsze rozumiał, główne przypadki, w których jest to właściwe instanceof są:Kiedy używasz instanceof właściwej decyzji?

  1. Wdrażanie Object.equals(Object). Więc jeśli pisałem klasę List i nie rozszerzałem AbstractList z jakiegokolwiek powodu, implementowałbym equals(o) przez najpierw testowanie o instanceof List, a następnie porównywanie elementów.
  2. Znaczna (algorytmiczne?) Optymalizacja dla szczególnego przypadku, dokłada nie semantykę zmiany, ale tylko wydajność. Na przykład: Collections.binarySearch wykonuje test instanceof RandomAccess i używa nieco innego wyszukiwania binarnego dla list RandomAccess i non-RandomAccess.

Nie sądzę, że instanceof reprezentuje zapach kodu w tych dwóch przypadkach. Ale czy są jakieś inne przypadki, w których rozsądnie jest używać instanceof?

Odpowiedz

6

kod Legacy lub API poza kontrolą są uzasadnione stosowanie -case dla instanceof. (Nawet wtedy wolałabym napisać nad nim warstwę OO, ale czasami czasami uniemożliwia to przeprojektowanie.)

W szczególności, fabryki oparte na hierarchiach klas zewnętrznych wydają się być powszechnym zastosowaniem.

+0

Hmmmmm. Czy masz jakieś przykłady? Nie spotkałem się z taką potrzebą z żadnymi interfejsami API, w które grałem. –

+0

@LouisWasserman Coś napisanego w sposób inny niż OO, na przykład, mieliśmy API innej firmy, w którym różne klasy FTP nie rozszerzały wspólnej klasy bazowej ani nie implementowały interfejsu. Aby traktować je jako "takie same", naszym szybkim obejściem było kilka banalnych sprawdzeń instancji. Ostatecznie nakładamy warstwę na wierzch. Na świecie jest mnóstwo złych API. –

+0

Ew. Sądzę, że powinienem być wdzięczny za to, że nie musiałem zajmować się żadną z tych bestii. –

-1

Jak już wspomniano, „poprawny” zastosowania instanceof są raczej ograniczone. O ile mi wiadomo, zasadniczo podsumowałeś dwa główne zastosowania.

Jednak można uogólniać swoje wypowiedzi nieco chociaż następująco:

  1. Type sprawdzanie przed niezbędnych odlewów.
  2. Wdrażanie specjalne scenariusze, które zależą od bardzo szczególnych przypadkach klasy
4

Twój pierwszy przypadek jest przykładem, w którym nie byłoby użyć operatora instanceof, ale sprawdzić, czy zajęcia są równe:

o != null && o.getClass() == this.getClass() 

Pozwoli to uniknąć sytuacji, w której wystąpienie A extends B i B jest uznawane za równe

Inne przypadki, które mogę od razu pomyśleć, ale jestem całkiem pewny, że v Obowiązuje przypadki są dostępne

  • przypadki fabryce, gdzie masz na przykład canCreate i create sposobu, który otrzymałeś ogólny interfejs jako parametr. Każda z fabryk może obsłużyć określoną implementację interfejsu, więc będzie wymagać instanceof. Definiowanie tylko interfejs w fabryce abstrakcyjnej klasy/interfejsu pozwala na przykład napisać kompozytu fabryka
  • implementacje kompozytowe (jak pokazano w moim pierwszym przykładzie)
+1

Ale w przypadku 'List', _want_ instancja' B rozciąga A' i 'C rozciąga A', aby była uważana za równą. –

+0

Twoja pierwsza uwaga jest czasami bardzo ważna. Istnieją jednak aplikacje, w których instancja klasy podrzędnej ma być "równa" instancji klasy nadrzędnej. W takim przypadku 'instanceof' ma więcej sensu niż sprawdzanie tożsamości klasy. Również 'instanceof' jest równoznaczne z sprawdzaniem tożsamości klasy dla klas końcowych. Nie testowałem dwóch metod, ale nie zdziwiłbym się, gdyby w takim przypadku 'instanceof' był odrobinę szybszy. –

+0

@LouisWasserman Ale 'instanceof' nie powiedzie się w takim przypadku:' anA instanceof aB' będzie "false". –

5

Jednym ze sposobów, aby odpowiedzieć na to pytanie byłoby odpowiedzieć „kiedy robi stałych stosować biblioteki Java instanceof?"Jeśli przyjmiemy, że Guava jest przykładem dobrze zaprojektowanej biblioteki Java, możemy sprawdzić, gdzie jest używana instanceof, aby zdecydować, kiedy jest to do przyjęcia.

Jeśli wyodrębnimy słoik kodu źródłowego Guawy i wyskoczymy, zobaczymy instanceof jest wymieniony 439 razy, przez 122 plików:

$ pwd 
/tmp/guava-13.0.1-sources 
$ grep -R 'instanceof' | wc -l 
439 
$ grep -Rl 'instanceof' | wc -l 
122 

I patrząc na niektóre z tych przypadków możemy zobaczyć kilka wzorów wyłaniają:

  • Aby sprawdzić równość

    Jest to najczęściej używane. Jest to w pewnym stopniu zależne od implementacji, ale zakładając, że faktycznie chcesz zmierzyć równość w oparciu o klasę/interfejs, który rozszerza/implementuje, możesz użyć instanceof, aby zapewnić obiekt, z którym pracujesz. Jednak może to potencjalnie powodować nieparzyste problemy, jeśli klasa podrzędna jest nadrzędna względem equals() i nie respektuje tych samych wymagań jako rodziców. Przykłady wszędzie, ale łatwym jest Lists.equalsImpl(), który jest używany przez ImmutableList.

  • do zwarcia niepotrzebnej budowy obiektu

    Można użyć instanceof aby sprawdzić, czy przekazany argument może być bezpiecznie stosowany lub zwrócone bez dalszego przekształcania go, na przykład, czy to już instancją pożądany klasy, lub jeśli wiemy, że jest niezmienna. Zobacz przykłady w CharMatcher.forPredicate(), Suppliers.memoize(), ImmutableList.copyOf() itp

  • Aby uzyskać dostęp do szczegółów implementacji bez narażania różne zachowanie

    Widać to w każdym miejscu w guawy, ale zwłaszcza w klasach użytkowych w statycznych pakiet com.google.common.collect, na przykład w Iterables.size(), gdzie wywołuje on Collection.size(), jeśli to możliwe, i w inny sposób zlicza liczbę elementów w iteratorze w czasie O(n).

  • Aby uniknąć wywoływania toString()

    jestem sceptyczny to zasługuje wykonywana w więcej niż bardzo kilku wybranych przypadków, ale zakładając, że jesteś pewien, że robisz to, co trzeba, można uniknąć niepotrzebnie przekształcając obiekty CharSequence w z instanceof, podobnie jak w przypadku Joiner.toString(Object).

  • Aby wykonać kompleksową obsługę

    Oczywiście „prawo” zrobić to użyć try/catch bloku (choć tak naprawdę, że robi instanceof kontrole już) wyjątku, ale czasami trzeba bardziej złożonej logiki manipulacyjną, która zasługuje używanie bloków warunkowych lub przekazywanie przetwarzania do osobnej metody, na przykład wyciąganie przyczyn lub przetwarzanie specyficzne dla implementacji. Przykład można znaleźć w SimpleTimeLimiter.throwCause().

Jedną rzeczą, która wyróżnia się patrząc na to zachowanie jest prawie wszystkie z nich są rozwiązywaniu problemów I nie powinno być rozwiązywanie. Są przydatne w kodzie biblioteki, np. w Iterables, ale jeśli implementuję to zachowanie, prawdopodobnie powinienem zadać sobie pytanie, czy nie ma tam bibliotek ani narzędzi, które rozwiązałyby to dla mnie.

We wszystkich przypadkach chciałbym powiedzieć, że kontrole instanceof powinny być używane wewnętrznie jako szczegół implementacji - oznacza to, że osoba dzwoniąca jakiejkolwiek metody, która opiera się na sprawdzeniu instanceof, nie powinna (łatwo) stwierdzić, że jest co zrobiłeś. Na przykład ImmutableList.copyOf() zawsze zwraca wartość ImmutableList. Jako szczegół implementacji używa on instanceof, aby uniknąć konstruowania nowych ImmutableList s, ale nie jest to konieczne, aby akceptowalnie zapewnić oczekiwane zachowanie.

Na marginesie, zabawne było natknięcie się na twoje imię, Louis, kiedy drążyłem kod źródłowy Guawy. Przysięgam, że nie miałem pojęcia!

+0

Niezła analiza dużej, szanowanej bazy kodów (Guava). – kevinarpe

+0

Biblioteka narzędzi nie zapewnia dobrej jakości próbkowania języka Java. Np. Ile wzorców projektowych GoF używa? –

+0

Użyłem Guava jako przykładu * dobrego * podstawy kodu, niekoniecznie reprezentatywnego. Jeśli istnieją inne * dobre * powody do używania 'instanceof', które przeoczyłem, zaktualizuję pytanie. W chwili obecnej nie jestem tego świadomy. – dimo414