web-dev-qa-db-de.com

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, den ich kenne, ist, wenn 'a' null ist, gibt der erste false zurück, während der zweite eine Ausnahme auslöst. Sonst geben sie immer das gleiche Ergebnis?

432
Megamug

Wenn Sie instanceof verwenden, müssen Sie die Klasse von B zur Kompilierungszeit kennen. Bei Verwendung von isAssignableFrom() kann es dynamisch sein und sich zur Laufzeit ändern.

471
Marc Novakowski

instanceof kann nur mit Referenztypen verwendet werden, nicht mit primitiven Typen. isAssignableFrom() kann mit allen Klassenobjekten verwendet werden:

a instanceof int  // syntax error
3 instanceof Foo  // syntax error
int.class.isAssignableFrom(int.class)  // true

Siehe http://Java.Sun.com/javase/6/docs/api/Java/lang/Class.html#isAssignableFrom (Java.lang.Class) .

201
Adam Rosenfield

In Bezug auf die Leistung sprechen:

TL; DR

Verwenden Sie isInstance oder instanceof mit ähnlicher Leistung. isAssignableFrom ist etwas langsamer.

Sortiert nach Leistung:

  1. isInstance
  2. Instanz von (+ 0,5%)
  3. isAssignableFrom (+ 2,7%)

Basierend auf einem Benchmark von 2000 Iterationen unter Java 8 Windows x64, mit 20 Aufwärm-Iterationen.

In der Theorie

Mit einem Soft-Like Bytecode-Viewer können wir jeden Operator in Bytecode übersetzen.

Im Zusammenhang mit:

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 wir messen, wie viele Bytecode-Anweisungen von jedem Operator verwendet werden, können wir erwarten, dass instanceof und isInstance schneller sind als isAssignableFrom . Die tatsächliche Leistung wird jedoch NICHT durch den Bytecode, sondern durch den Maschinencode (der plattformabhängig ist) bestimmt. Lassen Sie uns für jeden der Operatoren einen Mikro-Benchmark durchführen.

Der Benchmark

Credit: Wie von @ aleksandr-dubinsky empfohlen und dank @yura für die Bereitstellung des Basiscodes hier ein JMH Benchmark (siehe dieses Stimmführer ):

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

Es wurden die folgenden Ergebnisse erzielt (Punktzahl ist Anzahl der Operationen in einer Zeiteinheit, je höher 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 Vorgängen gibt, ist es möglich, auf einer anderen Java Version und/oder Plattformen wie Solaris, Mac oder Linux) ein anderes Ergebnis (und möglicherweise eine andere Reihenfolge!) 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 (wie B X erweitert, was Y erweitert, was Z A erweitert, können die Ergebnisse unterschiedlich sein).
  • in der Regel wird empfohlen, den Code zuerst zu schreiben, indem Sie einen der Operatoren auswählen (den bequemsten), und dann ein Profil für Ihren Code zu erstellen, um zu überprüfen, ob ein Leistungsengpass vorliegt. Vielleicht ist dieser Operator im Kontext Ihres Codes vernachlässigbar, oder vielleicht ...
  • in Bezug auf den vorherigen Punkt kann instanceof im Kontext Ihres Codes einfacher optimiert werden als beispielsweise ein isInstance ...

Um Ihnen ein Beispiel zu geben, nehmen Sie die folgende Schleife:

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 der JIT wird der Code irgendwann optimiert und wir erhalten:

  • instanz von: 6ms
  • isInstance: 12ms
  • isAssignableFrom: 15ms

Hinweis

Ursprünglich wurde für diesen Beitrag ein eigener Benchmark mit einer for - Schleife in Java durchgeführt, was zu unzuverlässigen Ergebnissen führte, da einige Optimierungen wie Just In Time die Schleife beseitigen können. Es wurde also hauptsächlich gemessen, wie lange der JIT-Compiler für die Optimierung der Schleife benötigt hat: Weitere Informationen finden Sie unter Leistungstest unabhängig von der Anzahl der Iterationen

Verwandte Fragen

107
JBE

Ein direkteres Äquivalent zu a instanceof B ist

B.class.isInstance(a)

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

33
user102008

Abgesehen von den oben erwähnten grundlegenden Unterschieden gibt es in Class einen subtilen Kernunterschied zwischen instanceof operator und isAssignableFrom-Methode.

Lesen Sie instanceof als "Ist dies (der linke Teil) die Instanz von diesem oder einer Unterklasse von diesem (der rechte Teil)" und lesen Sie x.getClass().isAssignableFrom(Y.class) als "Kann ich schreiben X x = new Y() ”. Mit anderen Worten, instanceof operator prüft, ob das linke Objekt dasselbe oder eine Unterklasse der rechten Klasse ist, während isAssignableFrom prüft, ob wir dem Verweis der Klasse, in der sich die Methode befindet, ein Objekt der Parameterklasse (von) zuweisen können namens.
Beachten Sie, dass beide die tatsächliche Instanz und nicht den Referenztyp berücksichtigen.

Betrachten Sie ein Beispiel für 3 Klassen A, B und C, wobei C B und B 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.
22
Ashish Arya

Es gibt noch einen weiteren Unterschied:

null Instanz von X ist false egal was X ist

null.getClass (). isAssignableFrom (X) löst eine NullPointerException aus

15
S. Ali Tokmen

Es gibt noch einen weiteren Unterschied. Wenn der zu testende Typ (Klasse) dynamisch ist, z. Übergeben als Methodenparameter, dann schneidet instanceof es nicht für Sie.

boolean test(Class clazz) {
   return (this instanceof clazz); // clazz cannot be resolved to a type.
}

aber du kannst tun:

boolean test(Class clazz) {
   return (clazz.isAssignableFrom(this.getClass())); // okidoki
}

Hoppla, ich sehe, dass diese Antwort bereits behandelt wurde. Vielleicht ist dieses Beispiel für jemanden hilfreich.

11
tkalmijn

Dieser Thread gab mir einen Einblick in die Unterschiede zwischen instanceof und isAssignableFrom, sodass ich dachte, ich würde etwas Eigenes teilen.

Ich habe festgestellt, dass die Verwendung von isAssignableFrom der einzige (wahrscheinlich nicht der einzige, aber möglicherweise der einfachste) Weg ist, sich selbst zu fragen, ob eine Referenz einer Klasse Instanzen einer anderen Klasse annehmen kann, wenn man Instanzen keiner Klasse hat um den Vergleich zu machen.

Daher fand ich die Verwendung des Operators instanceof zum Vergleichen der Zuweisbarkeit nicht als gute Idee, wenn ich nur Klassen hatte, es sei denn, ich überlegte, eine Instanz aus einer der Klassen zu erstellen. Ich dachte, das wäre schlampig.

7
Owen

Betrachten Sie die folgende Situation. Angenommen, Sie möchten überprüfen, ob Typ A eine Superklasse des Typs obj ist, dann können Sie entweder gehen

... A.class.isAssignableFrom (obj.getClass ()) ...

OR

... obj instanceof A ...

Für die isAssignableFrom-Lösung ist es jedoch erforderlich, dass der Typ des Objekts hier sichtbar ist. Ist dies nicht der Fall (z. B. könnte der Typ des Objekts eine private innere Klasse sein), ist diese Option nicht verfügbar. Die Instanz der Lösung würde jedoch immer funktionieren.

4
algebra

instanceof kann auch nicht mit primitiven oder generischen Typen verwendet werden. Wie im folgenden Code:

//Define Class< T > type ... 

Object e = new Object();

if(e instanceof T) {
  // Do something.
}

Der Fehler ist: Es kann keine Instanz der Prüfung für den Typparameter T durchgeführt werden. Verwenden Sie stattdessen das Löschobjekt, da weitere generische Typinformationen zur Laufzeit gelöscht werden.

Kompiliert nicht, da beim Löschen des Typs die Laufzeitreferenz entfernt wurde. Der folgende Code wird jedoch kompiliert:

if( type.isAssignableFrom(e.getClass())){
  // Do something.
}
4
James Drinkard
isAssignableFrom(A, B) =

if (A == B) return true
else if (B == Java.lang.Object) return false
else return isAssignableFrom(A, getSuperClass(B))

Der obige Pseudocode ist eine Definition, ob Referenzen von Typ/Klasse A von Referenzen von Typ/Klasse B zuweisbar sind. Es ist eine rekursive Definition. Für einige mag es hilfreich sein, für andere kann es verwirrend sein. Ich füge es hinzu, falls jemand es nützlich finden sollte. Dies ist nur ein Versuch, mein Verständnis zu erfassen, es ist nicht die offizielle Definition. Es wird in einer bestimmten Java VM) - Implementierung verwendet und funktioniert für viele Beispielprogramme. Ich kann also nicht garantieren, dass es alle Aspekte von isAssignableFrom erfasst, aber nicht ganz aus.

0

In Bezug auf die Leistung "2" (mit JMH):

class A{}
class B extends A{}

public class InstanceOfTest {

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

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

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

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

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

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

Es gibt:

Benchmark                            Mode  Cnt  Score   Error  Units
InstanceOfTest.testInstanceOf        avgt    5  1,972 ? 0,002  ns/op
InstanceOfTest.testIsAssignableFrom  avgt    5  1,991 ? 0,004  ns/op
InstanceOfTest.testIsInstance        avgt    5  1,972 ? 0,003  ns/op

Damit wir schließen können: instanceof so schnell wie isInstance () und isAssignableFrom () nicht weit entfernt (+ 0.9% Ausführungszeit). Also kein wirklicher Unterschied, was auch immer Sie wählen

0
Yura