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?
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.
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) .
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:
- isInstance
- par exemple: (+ 0.5%)
- 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'unisInstance
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
un équivalent plus direct de a instanceof B
est
B.class.isInstance(a)
cela fonctionne (retourne false) quand a
est null
aussi.
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.
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
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.
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é.
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.
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.
}
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.
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
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.