instancja Vs getClass ()

114

Widzę wzrost wydajności podczas używania getClass()i ==operatora nad instanceOfoperatorem.

Object  str = new Integer("2000");

long starttime = System.nanoTime();

if(str instanceof String) {
    System.out.println("its string");
} else {
    if (str instanceof Integer) {
        System.out.println("its integer");

    }
}

System.out.println((System.nanoTime()-starttime));

starttime = System.nanoTime();

if(str.getClass() == String.class) {
    System.out.println("its string in equals");
} else {
    if(str.getClass() == Integer.class) {
        System.out.println("its integer");
    }
}

System.out.println((System.nanoTime()-starttime));

Czy istnieją jakieś wytyczne, który z nich korzystać getClass()lub instanceOf?

Biorąc pod uwagę scenariusz: Wiem dokładnie dopasowane do klas, to znaczy String, Integer(są to zajęcia końcowe), etc.

Czy używanie instanceOfoperatora jest złą praktyką?

kropelka
źródło
3
Jest to wyjaśnione w: stackoverflow.com/questions/596462/… .
Clement P
2
Twoja metoda pomiaru czasu powoduje sztuczne opóźnienia i daje nieprawidłowe wyniki. Zamień kolejność sprawdzania, a zobaczysz, że pierwsza przeprowadzona kontrola (== lub instanceof) będzie zawsze dłuższa. Myślę, że to println () s. Nigdy nie powinieneś włączać tych rzeczy do swojego bloku czasowego.
kurtzmarc
Tylko jeden komentarz od siebie, aby porównać wydajność, użyj wielu iteracji cykli (np. 10000) w celu zwiększenia dokładności. Jedno wezwanie nie jest dobrym środkiem.
martins.tuga

Odpowiedzi:

140

Powodem, dla którego wydajność instanceofi getClass() == ...jest inna, jest to, że robią różne rzeczy.

  • instanceofsprawdza, czy odwołanie do obiektu po lewej stronie (LHS) jest wystąpieniem typu po prawej stronie (RHS), czy jakimś podtypem .

  • getClass() == ... sprawdza, czy typy są identyczne.

Dlatego zalecamy zignorowanie problemu z wydajnością i skorzystanie z alternatywy, która daje odpowiedź, której potrzebujesz.

Czy używanie instanceOfoperatora jest złą praktyką?

Niekoniecznie. Nadużywanie któregokolwiek z nich instanceOflub getClass() może oznaczać „zapach projektu”. Jeśli nie będziesz ostrożny, otrzymasz projekt, w którym dodanie nowych podklas powoduje znaczną ilość przeróbek kodu. W większości sytuacji preferowanym podejściem jest użycie polimorfizmu.

Jednak są przypadki, w których NIE są to „zapachy projektu”. Na przykład, equals(Object)musisz przetestować rzeczywisty typ argumentu i zwrócić, falsejeśli nie pasuje. Najlepiej to zrobić za pomocą getClass().


Pojęcia takie jak „najlepsza praktyka”, „zła praktyka”, „zapach projektu”, „anty-wzór” itd. Powinny być używane oszczędnie i traktowane z podejrzliwością. Zachęcają do czarno-białego myślenia. Lepiej jest wydawać sądy w kontekście, niż opierać się wyłącznie na dogmacie; np. coś, co ktoś powiedział, jest „najlepszą praktyką”.

Stephen C.
źródło
@StephenC Jak powiedziałeś, jest code smellto użycie albo. Oznacza to, że jest to konsekwencja złego projektu (niepolimorficznego) kodu, który zmusza cię do użycia. czy mogę w ten sposób wywnioskować użycie któregokolwiek z nich?
przewyższenie
@overexchange - 1) Powiedziałem „nadużywanie”, a nie „używanie”. 2) Poza tym nie rozumiem, o co pytasz. Co masz na myśli, mówiąc „wnioskuj użycie ...” ??? Kod albo używa tych rzeczy, albo nie.
Stephen C
Wnioskuję z tego, że użycie instanceof& getClass()pojawia się z powodu istniejącego złego projektu (niepolimorficznego) kodu. mam rację?
przewymiarowanie
5
@overexchange - nie można zasadnie wywnioskować, że każde użycie instanceof(na przykład) jest złym projektem. Są sytuacje, w których może to być najlepsze rozwiązanie. To samo dotyczy getClass(). Powtórzę, że powiedziałem „nadużywać”, a nie „używać” . Każdy przypadek należy oceniać merytorycznie ... a nie ślepo stosując jakąś nieuzasadnioną dogmatyczną zasadę.
Stephen C
44

Czy chcesz dokładnie dopasować klasę , np. Tylko dopasowywanie FileInputStreamzamiast dowolnej podklasy FileInputStream? Jeśli tak, użyj getClass()i ==. Zazwyczaj robiłbym to w an equals, więc wystąpienie X nie jest uważane za równe wystąpieniu podklasy X - w przeciwnym razie możesz dostać się do trudnych problemów z symetrią. Z drugiej strony jest to bardziej przydatne do porównywania, że ​​dwa obiekty należą do tej samej klasy niż jednej określonej klasy.

W przeciwnym razie użyj instanceof. Zauważ, getClass()że będziesz musiał upewnić się, że masz niezerowe odniesienie, od którego zaczniesz, w przeciwnym razie otrzymasz a NullPointerException, podczas gdy instanceofzwróci po prostu, falsejeśli pierwszy operand ma wartość null.

Osobiście powiedziałbym, że instanceofjest bardziej idiomatyczny - ale intensywne używanie któregokolwiek z nich jest w większości przypadków zapachem projektowym.

Jon Skeet
źródło
18

Wiem, że minęło trochę czasu, odkąd o to zapytano, ale wczoraj nauczyłem się alternatywy

Wszyscy wiemy, że możesz:

if(o instanceof String) {   // etc

ale co, jeśli nie wiesz dokładnie, jaki to rodzaj zajęć? nie możesz generalnie zrobić:

if(o instanceof <Class variable>.getClass()) {   

ponieważ daje błąd kompilacji.
Zamiast tego tutaj jest alternatywa - isAssignableFrom ()

Na przykład:

public static boolean isASubClass(Class classTypeWeWant, Object objectWeHave) {

    return classTypeWeWant.isAssignableFrom(objectWeHave.getClass())
}
Andy Dingfelder
źródło
8
Nie używaj isAssignableFrom. Prawidłowy sposób pisania o instanceof Stringza pomocą refleksji to String.getClass().isInstance(o). Tak mówi javadoc: ta metoda jest dynamicznym odpowiednikiem instanceofoperatora języka Java .
Andreas
3

getClass () ma ograniczenie, że obiekty są równe tylko innym obiektom tej samej klasy, tego samego typu w czasie wykonywania, jak pokazano na wyjściu poniższego kodu:

class ParentClass{
}
public class SubClass extends ParentClass{
    public static void main(String []args){
        ParentClass parentClassInstance = new ParentClass();
        SubClass subClassInstance = new SubClass();
        if(subClassInstance instanceof ParentClass){
            System.out.println("SubClass extends ParentClass. subClassInstance is instanceof ParentClass");
        }
        if(subClassInstance.getClass() != parentClassInstance.getClass()){
            System.out.println("Different getClass() return results with subClassInstance and parentClassInstance ");
        }
    }
}

Wyjścia:

SubClass rozszerza ParentClass. subClassInstance jest instancją ParentClass.

Różne wyniki funkcji getClass () zwracają z subClassInstance i parentClassInstance.

Saurav Sahu
źródło