509 votes

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

Laquelle des propositions suivantes est la meilleure ?

a instanceof B

ou

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

La seule différence que je connaisse est que, lorsque 'a' est nul, la première renvoie false, tandis que la seconde lève une exception. À part cela, donnent-elles toujours le même résultat ?

17 votes

Pour les enregistrements, isInstance() est la méthode la plus pratique pour vérifier si un objet peut être casté dans un type de classe (pour plus de détails, voir : tshikatshikaaa.blogspot.nl/2012/07/ )

2 votes

Pour contrer ce que @JérômeVerstrynge a suggéré : La construction instanceof est un moyen privilégié de vérifier si une variable peut être castée vers un certain type de manière statique. car une erreur de compilation se produira en cas de types incompatibles. La méthode isInstance() de java.lang.Class fonctionne différemment et ne vérifie le type qu'au moment de l'exécution. Les types incompatibles ne seront donc pas détectés au début du développement, ce qui peut entraîner un code mort. La méthode isInstance() ne doit être utilisée que dans les cas dynamiques où l'opérateur instanceof ne peut pas être utilisé.

556voto

Marc Novakowski Points 22611

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

19 votes

Je ne comprends pas - veuillez développer. pourquoi nous ne pouvons pas écrire a instanceof Bref.getClass() . comment cela peut-il être la réponse acceptée avec si peu d'explication (ou son absence) ?

80 votes

La syntaxe est la suivante a instanceof Bref pas a instanceof Bref.class . Le second argument de l'opérateur instanceof est un nom de classe, et non une expression résolvant une instance d'objet de classe.

2 votes

Oui "dynamique" va de soi :) En dehors des performances, c'est une vraie différence.

222voto

Adam Rosenfield Points 176408

instanceof ne peut être utilisé qu'avec des types de référence, et non des types primitifs. isAssignableFrom() peut être utilisé avec n'importe quel objet 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) .

16 votes

Je ne vois pas l'intérêt d'utiliser instanceof/isAssignableFrom avec les types primitifs.

123voto

JBE Points 998

En termes de performances :

TL;DR

Utilice isInstance o instanceof qui ont des performances similaires. isAssignableFrom est légèrement plus lent.

Classés par performance :

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

Basé sur un benchmark de 2000 itérations sur JAVA 8 Windows x64, avec 20 itérations de réchauffement.

En théorie

Utilisation d'un produit doux comme visualisation du bytecode 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);

En mesurant le nombre d'instructions de bytecode utilisées par chaque opérateur, on peut s'attendre à ce que instanceof y isInstance pour être plus rapide que isAssignableFrom . Cependant, les performances réelles ne sont PAS déterminées par le bytecode mais par le code machine (qui dépend de la plate-forme). Faisons un micro benchmark pour chacun des opérateurs.

L'indice de référence

Crédit : Comme conseillé par @aleksandr-dubinsky, et grâce à @yura qui a fourni le code de base, voici une JMH (voir ce 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 (le score est un nombre d'opérations dans une unité de temps (donc plus le score est élevé, mieux c'est) :

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

  • le benchmark est dépendant de la JVM et de la plateforme. Comme il n'y a pas de différences significatives entre chaque opération, il est possible d'obtenir un résultat différent (et peut-être un ordre différent !) sur une version différente de JAVA et/ou sur des plateformes différentes comme Solaris, Mac ou Linux.
  • le benchmark compare les performances de "is B an instance of A" quand "B extends A" directement. Si la hiérarchie des classes 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 code en choisissant d'abord l'un des opérateurs (le plus pratique), puis de profiler votre code pour vérifier s'il y a un goulot d'étranglement au niveau des performances. 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'une 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
  • isAssignableFrom : 15ms

Note

À l'origine, ce post faisait son propre benchmark en utilisant un pour en JAVA brut, ce qui donnait des résultats peu fiables car certaines optimisations comme Just In Time peuvent éliminer la boucle. Il s'agissait donc surtout de mesurer combien de temps le compilateur JIT mettait à optimiser la boucle : cf. Test de performance indépendant du nombre d'itérations pour plus de détails

Questions connexes

8 votes

Ouaip, instanceof est un bytecode qui utilise essentiellement la même logique que checkcast (le bytecode derrière le casting). Il sera intrinsèquement plus rapide que les autres options, quel que soit le degré d'optimisation du JITC.

3 votes

Ce qui est logique, puisque isAssignableFrom() est dynamique.

0 votes

Yep, avec JMH les résultats sont complètement différents (même vitesse pour tous).

36voto

user102008 Points 8748

Un équivalent plus direct de a instanceof B est

B.class.isInstance(a)

Cela fonctionne (renvoie faux) lorsque a es null aussi.

2 votes

Cool, mais cela ne répond pas à la question et aurait dû être un commentaire.

31voto

Ashish Arya Points 187

Outre les différences fondamentales mentionnées ci-dessus, il existe une différence essentielle et subtile entre l'opérateur instanceof et la méthode isAssignableFrom dans les classes.

Lire instanceof comme "est-ce que ceci (la partie gauche) est l'instance de ceci ou une sous-classe de ceci (la partie droite)" et lire x.getClass().isAssignableFrom(Y.class) comme "Puis-je écrire X x = new Y() ". En d'autres termes, l'opérateur instanceof vérifie si l'objet de gauche est le même ou un sous-ensemble de la classe de droite, alors que isAssignableFrom vérifie si on peut assigner l'objet de la classe du paramètre (from) à la référence de la classe sur laquelle la méthode est appelée.
Notez que toutes deux considèrent l'instance réelle et non le type de référence.

Considérons un exemple de 3 classes A, B et C où C étend B et B é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.

9 votes

b instanceof A est équivalent à A.class.isAssignableFrom(b.getClass()) (comme l'a remarqué le PO). Votre exemple est correct mais non pertinent.

0 votes

Depuis new Y() peut ne pas être légal si Y est abstraite ou sans constructeur public par défaut, vous pouvez dire que X x = (Y)null est légal si et seulement si x.getClass().isAssignableFrom(Y.class) est vrai.

2 votes

Pourquoi 'b.getClass().isAssignableFrom(A.class)' dans cet exemple ? Je pense que l'exemple devrait être inversé A.class.isAssignableFrom(b.getClass()).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X