Instanz von Vs getClass ()

113

Ich sehe einen Leistungsgewinn bei der Verwendung von getClass()und ==Operator über instanceOfOperator.

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));

Gibt es eine Richtlinie, welche zu verwenden ist getClass()oder instanceOf?

In einem vorgegebenen Szenario: Ich weiß genau Klassen angepasst werden, das heißt String, Integer(diese sind final Klassen) usw.

Ist die Verwendung des instanceOfBedieners eine schlechte Praxis?

Klecks
quelle
3
Dies wird erklärt in: stackoverflow.com/questions/596462/… .
Clement P
2
Ihre Timing-Methode verursacht künstliche Verzögerungen und führt zu falschen Timing-Ergebnissen. Tauschen Sie die Reihenfolge aus, in der Sie die Prüfungen durchführen, und Sie werden sehen, dass die erste Prüfung, die Sie durchführen (entweder == oder instanceof), immer länger ist. Ich würde vermuten, es sind die println () s. Sie sollten dieses Zeug niemals in Ihren Zeitblock aufnehmen.
Kurtzmarc
Verwenden Sie zum Vergleich der Leistung nur einen Kommentar. Verwenden Sie mehrere Zyklusiterationen (z. B. 10000), um die Genauigkeit zu verbessern. Ein einziger Aufruf ist keine gute Maßnahme.
martins.tuga

Antworten:

138

Der Grund, warum die Leistung von instanceofund getClass() == ...unterschiedlich ist, ist, dass sie unterschiedliche Dinge tun.

  • instanceofTestet, ob die Objektreferenz auf der linken Seite (LHS) eine Instanz des Typs auf der rechten Seite (RHS) oder ein Subtyp ist .

  • getClass() == ... prüft, ob die Typen identisch sind.

Die Empfehlung lautet daher, das Leistungsproblem zu ignorieren und die Alternative zu verwenden, die Ihnen die Antwort gibt, die Sie benötigen.

Ist die Verwendung des instanceOfBedieners eine schlechte Praxis?

Nicht unbedingt. Überbeanspruchung von entweder instanceOfoder getClass() kann "Designgeruch" sein. Wenn Sie nicht vorsichtig sind, erhalten Sie ein Design, bei dem das Hinzufügen neuer Unterklassen zu einer erheblichen Überarbeitung des Codes führt. In den meisten Situationen besteht der bevorzugte Ansatz darin, Polymorphismus zu verwenden.

Es gibt jedoch Fälle, in denen dies KEIN "Designgeruch" ist. In müssen Sie beispielsweise equals(Object)den tatsächlichen Typ des Arguments testen und zurückgeben, falsewenn es nicht übereinstimmt. Dies geschieht am besten mit getClass().


Begriffe wie "Best Practice", "Bad Practice", "Designgeruch", "Antipattern" usw. sollten sparsam verwendet und mit Argwohn behandelt werden. Sie fördern das Schwarz-Weiß-Denken. Es ist besser, Ihre Urteile im Kontext zu fällen, als nur auf Dogmen zu beruhen. zB etwas, das jemand gesagt hat, ist "Best Practice".

Stephen C.
quelle
@StephenC Wie Sie sagten, ist code smelles entweder zu verwenden. Das heißt, es ist eine Folge von schlechtem Design-Code (nicht polymorph), der Sie dazu bringt, beide zu verwenden. Kann ich auf diese Weise auf die Verwendung von beiden schließen?
Überaustausch
@overexchange - 1) Ich sagte "Überbeanspruchung" nicht "Verwendung". 2) Abgesehen davon verstehe ich nicht, was Sie fragen. Was meinst du mit "auf die Verwendung schließen ..." ??? Code verwendet diese Dinge entweder oder nicht.
Stephen C
Ich schließe daraus, dass die Verwendung von instanceof& getClass()aufgrund des vorhandenen schlechten Designs (nicht polymorph) des Codes ins Bild kommt. hab ich recht?
Überaustausch
5
@overexchange - Sie können nicht gültig schließen, dass jede Verwendung von instanceof(zum Beispiel) schlechtes Design ist. Es gibt Situationen, in denen dies die beste Lösung sein kann. Gleiches gilt für getClass(). Ich werde wiederholen, dass ich "Überbeanspruchung" und nicht "Verwendung" gesagt habe . Jeder Fall muss nach seinen Vorzügen beurteilt werden ... nicht durch blinde Anwendung einer unbegründeten dogmatischen Regel.
Stephen C
43

Möchten Sie eine Klasse genau zuordnen , z. B. nur eine Übereinstimmung FileInputStreamanstelle einer Unterklasse von FileInputStream? Wenn ja, verwenden Sie getClass()und ==. Ich würde dies normalerweise in a tun equals, damit eine Instanz von X nicht gleich einer Instanz einer Unterklasse von X ist - andernfalls kann es zu kniffligen Symmetrieproblemen kommen. Andererseits ist dies normalerweise nützlicher, um zu vergleichen, dass zwei Objekte derselben Klasse angehören als einer bestimmten Klasse.

Andernfalls verwenden Sie instanceof. Beachten Sie, dass getClass()Sie zunächst sicherstellen müssen, dass Sie eine Nicht-Null-Referenz haben, sonst erhalten Sie eine NullPointerException, während instanceofnur zurückgegeben wird, falsewenn der erste Operand Null ist.

Persönlich würde ich sagen, dass instanceofes idiomatischer ist - aber eine der beiden ausgiebig zu verwenden, ist in den meisten Fällen ein Designgeruch.

Jon Skeet
quelle
18

Ich weiß, dass es eine Weile her ist, seit dies gefragt wurde, aber ich habe gestern eine Alternative gelernt

Wir alle wissen, dass Sie Folgendes tun können:

if(o instanceof String) {   // etc

aber was ist, wenn Sie nicht genau wissen, welche Art von Klasse es sein muss? Sie können nicht generisch tun:

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

da es einen Kompilierungsfehler gibt.
Stattdessen gibt es hier eine Alternative - isAssignableFrom ()

Beispielsweise:

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

    return classTypeWeWant.isAssignableFrom(objectWeHave.getClass())
}
Andy Dingfelder
quelle
8
Nicht benutzen isAssignableFrom. Die richtige Art, o instanceof Stringmit Reflexion zu schreiben , ist String.getClass().isInstance(o). Das Javadoc sagt es sogar so: Diese Methode ist das dynamische Äquivalent des Java- instanceofSprachoperators.
Andreas
3

getClass () hat die Einschränkung, dass Objekte nur anderen Objekten derselben Klasse und demselben Laufzeittyp entsprechen, wie in der Ausgabe des folgenden Codes dargestellt:

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 ");
        }
    }
}

Ausgänge:

SubClass erweitert ParentClass. subClassInstance ist eine Instanz von ParentClass.

Verschiedene getClass () geben Ergebnisse mit subClassInstance und parentClassInstance zurück.

Saurav Sahu
quelle