Quelle est la différence entre instanceof et Class.isAssignableFrom(...)?

lequel des suivants est le meilleur?

a instanceof B

ou

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

la seule différence que je connaisse est, quand 'a' est nul, le premier retourne false, tandis que le second jette une exception. À part cela, donnent-ils toujours le même résultat?

408
demandé sur Megamug 2009-01-30 22:44:24

13 réponses

lorsque vous utilisez instanceof , vous devez connaître la classe de B au moment de la compilation. Lorsqu'on utilise isAssignableFrom() , il peut être dynamique et changer pendant l'exécution.

443
répondu Marc Novakowski 2016-10-02 20:04:33

instanceof ne peut être utilisé qu'avec des types de référence, pas des types primitifs. isAssignableFrom() peut être utilisé avec tous les objets de classe:

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

voir http://java.sun.com/javase/6/docs/api/java/lang/Class.html#isAssignableFrom(java.lang.Class) .

198
répondu Adam Rosenfield 2009-01-30 19:53:16

Parler en termes de performance :

TL; DR

Utiliser isInstance ou instanceof qui ont des performances similaires. isassignable de est légèrement plus lent.

Trié par performance:

  1. isInstance
  2. par exemple: (+ 0.5%)
  3. isassignable from (+ 2.7%)

basé sur un benchmark de 2000 itérations sur JAVA 8 Windows x64, avec 20 itérations d'échauffement.

en théorie

en utilisant un soft comme bytecode viewer nous pouvons traduire chaque opérateur en bytecode.

dans le contexte de:

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

mesurant combien d'instructions de bytecode sont utilisées par chaque opérateur, nous pourrions nous attendre à instanceof et est par exemple d'être plus rapide que isassignable de . Toutefois, les performances réelles ne sont pas déterminées par le code de la machine, mais par le code de la machine (qui dépend de la plate-forme). Faisons un micro benchmark pour chacun des opérateurs.

La référence

crédit: comme conseillé par @aleksandr-dubinsky, et grâce à @yura pour fournir le code de base, voici un JMH indice de référence (voir cette guide de réglage ):

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

a donné les résultats suivants (la note est un certain nombre d'opérations dans une unité de temps , de sorte que plus le score est élevé le mieux):

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

Avertissement

  • l'indice de référence est JVM et dépend de la plate-forme. Depuis il n'y a pas de différences significatives entre chaque opération, il pourrait être possible d'obtenir un résultat différent (et peut-être différent de l'ordre!) sur une version JAVA différente et/ou sur des plateformes comme Solaris, Mac ou Linux.
  • l'indice de référence compare les performances de l'est B une instance d'Un" quand "B extends A" directement. Si la hiérarchie de classe est plus profonde et plus complexe (comme B étend X qui étend Y qui étend Z qui étend A), les résultats peuvent être différents.
  • il est généralement conseillé d'écrire le choisissez d'abord l'un des opérateurs (le plus pratique) et ensuite profilez votre code pour vérifier s'il y a un goulot d'étranglement de performance. Peut-être que cet opérateur est négligeable dans le contexte de votre code, ou peut-être...
  • par rapport au point précédent, instanceof dans le contexte de votre code pourrait être optimisé plus facilement qu'un isInstance par exemple...

pour vous donner un exemple, prenez la boucle suivante:

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;

grâce au JIT, le code est optimisé à un moment donné et nous obtenons:

  • instanceof: 6ms
  • isInstance: 12ms
  • isassignable de: 15ms

Note

à l'origine, ce post faisait son propre benchmark en utilisant une boucle pour dans le JAVA brut, qui a donné des résultats peu fiables comme une optimisation comme Just In Time peut éliminer la boucle. Il s'agissait donc surtout de mesurer le temps que prenait le compilateur JIT pour optimiser la boucle: voir test de Performance indépendant du nombre d'itérations pour plus de détails

questions connexes

98
répondu JBE 2018-07-27 15:42:09

un équivalent plus direct de a instanceof B est

B.class.isInstance(a)

cela fonctionne (retourne false) quand a est null aussi.

33
répondu user102008 2011-08-15 20:58:53

en dehors des différences de base mentionnées ci-dessus, il y a une différence subtile entre l'instanceof operator et isassignable from method in Class.

Lire instanceof comme "est-ce (la partie de gauche) l'instance de cette ou de toute sous-classe de ceci (la partie de droite)" et lire x.getClass().isAssignableFrom(Y.class) comme "Puis-je écrire X x = new Y() ". En d'autres termes, instanceof operator vérifie si l'objet de gauche est le même ou la sous-classe de la classe droite, tandis que isAssignableFrom vérifie si nous pouvons assigner objet de la classe de paramètres (de) à la référence de la classe sur lequel la méthode est appelée.

Notez que ces deux examinez l'exemple pas le type de référence.

prendre en considération un exemple de 3 classes A, B et C où C s'étend B et B s'étend A.

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.
23
répondu Ashish Arya 2011-03-07 16:04:36

il y a aussi une autre différence:

null instanceof X est false peu importe ce que X est

null.getClass ().isassignable from (X) lancera une NullPointerException

14
répondu S. Ali Tokmen 2011-11-01 17:00:01

Il y a encore une autre différence. Si le type (classe) à tester est dynamique, par exemple passé comme paramètre de méthode, instanceof ne le coupera pas pour vous.

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

mais vous pouvez faire:

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

Oups, je vois que cette réponse est déjà couverte. Peut-être cet exemple est utile à quelqu'un.

9
répondu tkalmijn 2011-01-25 16:16:10

ce fil m'a donné un aperçu de la façon dont instanceof différait de isAssignableFrom , donc j'ai pensé que je partagerais quelque chose de mon propre.

j'ai trouvé que l'utilisation de isAssignableFrom pour être la seule (probablement pas la seule, mais peut-être la plus facile) façon de se demander si une référence d'une classe peut prendre des instances d'une autre, quand on a des instances de Ni l'une ni l'autre classe pour faire la comparaison.

donc, je n'ai pas trouvé en utilisant le instanceof opérateur de comparer assignability pour être une bonne idée quand tout ce que j'avais étaient des classes, à moins que je n'ai envisagé de créer une instance à partir d'une des classes; j'ai pensé que ce serait bâclé.

7
répondu Owen 2011-12-08 18:47:26

envisager la situation suivante. Supposons que vous voulez vérifier si le type A est une super classe du type obj, vous pouvez aller soit

... A.class.isassignable from (obj.getClass()) ...

ou

... obj instanceof Un ...

mais l'isassignable de solution exige que le type d'obj soit visible ici. Si ce n'est pas le cas (par exemple, le type d'obj pourrait être privé intérieur de la classe), ce l'option est désactivée. Cependant, l'instancede la solution fonctionnerait toujours.

4
répondu algebra 2010-03-18 20:14:26

instanceof ne peut pas non plus être utilisé avec des types primitifs ou des types génériques. Comme dans le code suivant:

//Define Class< T > type ... 

Object e = new Object();

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

l'erreur est: ne peut pas effectuer instanceof check contre le paramètre de type T. utilisez plutôt son objet erasure car d'autres informations de type générique seront effacées à l'exécution.

ne se compile pas en raison de l'effacement de type supprimant la référence d'exécution. Cependant, le code ci-dessous compilera:

if( type.isAssignableFrom(e.getClass())){
  // Do something.
}
3
répondu James Drinkard 2016-01-05 20:49:45
isAssignableFrom(A, B) =

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

le pseudo-code ci-dessus est une définition de, si les références de type/Classe A sont assignables à partir des références de type/classe B. Il s'agit d'une définition récursive. Pour certains, cela peut être utile, pour d'autres, cela peut prêter à confusion. Je l'ajoute au cas où quelqu'un le trouverait utile. Ce n'est qu'une tentative pour saisir ma compréhension, ce n'est pas la définition officielle. Il est utilisé dans une certaine implémentation Java VM et fonctionne pour de nombreux programmes d'exemple, donc bien que je ne puisse pas garantir qu'il capte tous les aspects de isassignable from, Il n'est pas complètement éteint.

0
répondu Stephan Korsholm 2016-04-12 20:00:25

Parler en termes de performance "2" (avec 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();
}
}

il donne:

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

, de Sorte que nous pouvons conclure: instanceof aussi vite que isInstance() et isAssignableFrom() pas loin (+0.9% executon temps). Donc, aucune différence réelle quoi que vous choisissiez

0
répondu Yura 2017-08-20 17:05:04

certains tests que nous avons effectués dans notre équipe montrent que A.class.isAssignableFrom(B.getClass()) fonctionne plus vite que B instanceof A . cela peut être très utile si vous avez besoin de le vérifier sur grand nombre d'éléments.

-2
répondu Milan 2015-05-15 10:44:13