510 Stimmen

Was ist der Unterschied zwischen instanceof und Class.isAssignableFrom(...)?

Welche der folgenden Möglichkeiten ist besser?

a instanceof B

oder

B.class.isAssignableFrom(a.getClass())

Der einzige Unterschied, der mir bekannt ist, besteht darin, dass die erste Methode false zurückgibt, wenn 'a' null ist, während die zweite eine Ausnahme auslöst. Liefern sie sonst immer das gleiche Ergebnis?

17 Stimmen

F t )

2 Stimmen

Téô T '

556voto

Marc Novakowski Punkte 43303

Bei der Verwendung von instanceof müssen Sie die Klasse von B zur Kompilierzeit. Bei der Verwendung von isAssignableFrom() Sie kann dynamisch sein und sich während der Laufzeit ändern.

123voto

JBE Punkte 10704

In Bezug auf die Leistung:

TL;DR

Verwenden Sie isInstance ou instanceof die eine ähnliche Leistung aufweisen. isAssignableFrom ist etwas langsamer.

Sortiert nach Leistung:

  1. isInstance
  2. instanceof (+ 0.5%)
  3. isAssignableFrom (+ 2.7%)

Basierend auf einem Benchmark von 2000 Iterationen auf JAVA 8 Windows x64, mit 20 Aufwärmiterationen.

Theoretisch

Mit einem weichen wie Bytecode-Betrachter können wir jeden Operator in Bytecode übersetzen.

Im Kontext von:

package foo;

public class Benchmark
{
  public static final Object a = new A();
  public static final Object b = new B();

  ...

}

JAVA:

b instanceof A;

Bytecode:

getstatic foo/Benchmark.b:java.lang.Object
instanceof foo/A

JAVA:

A.class.isInstance(b);

Bytecode:

ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:java.lang.Object
invokevirtual java/lang/Class isInstance((Ljava/lang/Object;)Z);

JAVA:

A.class.isAssignableFrom(b.getClass());

Bytecode:

ldc Lfoo/A; (org.objectweb.asm.Type)
getstatic foo/Benchmark.b:java.lang.Object
invokevirtual java/lang/Object getClass(()Ljava/lang/Class;);
invokevirtual java/lang/Class isAssignableFrom((Ljava/lang/Class;)Z);

Wenn man misst, wie viele Bytecode-Anweisungen von jedem Operator verwendet werden, könnte man erwarten instanceof y isInstance schneller zu sein als isAssignableFrom . Die tatsächliche Leistung wird jedoch NICHT durch den Bytecode bestimmt, sondern durch den Maschinencode (der plattformabhängig ist). Führen wir für jeden der Operatoren einen Mikro-Benchmark durch.

Die Benchmark

Credit: Wie von @aleksandr-dubinsky empfohlen und mit Dank an @yura für die Bereitstellung des Basiscodes, ist hier ein JMH Benchmark (siehe diese Tuning-Leitfaden ):

class A {}
class B extends A {}

public class Benchmark {

    public static final Object a = new A();
    public static final Object b = new B();

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public boolean testInstanceOf()
    {
        return b instanceof A;
    }

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public boolean testIsInstance()
    {
        return A.class.isInstance(b);
    }

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public boolean testIsAssignableFrom()
    {
        return A.class.isAssignableFrom(b.getClass());
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(TestPerf2.class.getSimpleName())
                .warmupIterations(20)
                .measurementIterations(2000)
                .forks(1)
                .build();

        new Runner(opt).run();
    }
}

Ergab die folgenden Ergebnisse (die Punktzahl ist eine Anzahl von Vorgängen in einer Zeiteinheit je höher also die Punktzahl, desto besser):

Benchmark                       Mode   Cnt    Score   Error   Units
Benchmark.testIsInstance        thrpt  2000  373,061 ± 0,115  ops/us
Benchmark.testInstanceOf        thrpt  2000  371,047 ± 0,131  ops/us
Benchmark.testIsAssignableFrom  thrpt  2000  363,648 ± 0,289  ops/us

Warnung

  • der Benchmark ist JVM- und plattformabhängig. Da es keine signifikanten Unterschiede zwischen den einzelnen Operationen gibt, kann es möglich sein, ein anderes Ergebnis (und vielleicht eine andere Reihenfolge!) auf einer anderen JAVA-Version und/oder Plattform wie Solaris, Mac oder Linux zu erhalten.
  • der Benchmark vergleicht die Leistung von "ist B eine Instanz von A", wenn "B erweitert A" direkt. Wenn die Klassenhierarchie tiefer und komplexer ist (z. B. B erweitert X, das Y erweitert, das Z erweitert, das A erweitert), könnten die Ergebnisse anders ausfallen.
  • ist es in der Regel ratsam, den Code zuerst zu schreiben und dabei einen der Operatoren (den bequemsten) zu wählen und dann ein Profil zu erstellen, um zu prüfen, ob es einen Leistungsengpass gibt. Vielleicht ist dieser Operator im Zusammenhang mit Ihrem Code vernachlässigbar, oder vielleicht...
  • in Bezug auf den vorherigen Punkt, instanceof im Kontext Ihres Codes möglicherweise leichter optimiert werden als eine isInstance zum Beispiel...

Als Beispiel sei die folgende Schleife genannt:

class A{}
class B extends A{}

A b = new B();

boolean execute(){
  return A.class.isAssignableFrom(b.getClass());
  // return A.class.isInstance(b);
  // return b instanceof A;
}

// Warmup the code
for (int i = 0; i < 100; ++i)
  execute();

// Time it
int count = 100000;
final long start = System.nanoTime();
for(int i=0; i<count; i++){
   execute();
}
final long elapsed = System.nanoTime() - start;

Dank JIT wird der Code an einem bestimmten Punkt optimiert und wir erhalten:

  • instanceof: 6ms
  • isInstance: 12ms
  • isAssignableFrom : 15ms

Hinweis

Ursprünglich wurde in diesem Beitrag ein eigener Benchmark mit einer für Schleife in rohem JAVA, was zu unzuverlässigen Ergebnissen führte, da eine Optimierung wie Just In Time die Schleife eliminieren kann. Es wurde also hauptsächlich gemessen, wie lange der JIT-Compiler für die Optimierung der Schleife brauchte: siehe Leistungstest unabhängig von der Anzahl der Iterationen für weitere Einzelheiten

Verwandte Fragen

36voto

user102008 Punkte 29575

Ein direkteres Äquivalent zu a instanceof B es

B.class.isInstance(a)

Dies funktioniert (gibt false zurück), wenn a es null auch.

31voto

Ashish Arya Punkte 341

Abgesehen von den oben erwähnten grundlegenden Unterschieden gibt es einen wesentlichen subtilen Unterschied zwischen dem instanceof-Operator und der Methode isAssignableFrom in Class.

Lesen Sie instanceof als "ist dies (der linke Teil) die Instanz von dies oder eine Unterklasse von dies (der rechte Teil)" und lesen x.getClass().isAssignableFrom(Y.class) wie "Kann ich schreiben X x = new Y() ". Mit anderen Worten: Der instanceof-Operator prüft, ob das linke Objekt dieselbe oder eine Unterklasse der rechten Klasse ist, während isAssignableFrom prüft, ob ein Objekt der Parameterklasse (von) der Referenz der Klasse, auf der die Methode aufgerufen wird, zugewiesen werden kann.
Beachten Sie, dass in beiden Fällen die tatsächliche Instanz und nicht der Referenztyp berücksichtigt wird.

Betrachten wir ein Beispiel mit 3 Klassen A, B und C, wobei C die Klasse B und B die Klasse A erweitert.

B b = new C();

System.out.println(b instanceof A); //is b (which is actually class C object) instance of A, yes. This will return true.  
System.out.println(b instanceof B); // is b (which is actually class C object) instance of B, yes. This will return true.  
System.out.println(b instanceof C); // is b (which is actually class C object) instance of C, yes. This will return true. If the first statement would be B b = new B(), this would have been false.
System.out.println(b.getClass().isAssignableFrom(A.class));//Can I write C c = new A(), no. So this is false.
System.out.println(b.getClass().isAssignableFrom(B.class)); //Can I write C c = new B(), no. So this is false.
System.out.println(b.getClass().isAssignableFrom(C.class)); //Can I write C c = new C(), Yes. So this is true.

14voto

S. Ali Tokmen Punkte 167

Es gibt noch einen weiteren Unterschied:

null instanceof X ist false egal, was X ist

null.getClass().isAssignableFrom(X) wird eine NullPointerException auslösen

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X