Question

Lequel des éléments suivants est mieux?

a instanceof B

ou

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

La seule différence que je connaisse est, quand « a » est nulle, les premiers retours de faux, tandis que le second lance une exception. A part cela, ils donnent toujours le même résultat?

Était-ce utile?

La solution

Lors de l'utilisation instanceof, vous devez connaître la classe de B à la compilation. Lors de l'utilisation, il peut être isAssignableFrom() dynamique et changer au cours de l'exécution.

Autres conseils

instanceof ne peut être utilisé avec des types de référence, pas les types primitifs. Peut être utilisé isAssignableFrom() avec des 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

Utilisez isInstance ou instanceof qui ont des performances similaires. IsAssignableFrom est légèrement plus lente.

Trié par la performance:

  1. isInstance
  2. instanceof (+ 0,5%)
  3. IsAssignableFrom (+ 2,7%)

Basé sur une référence de 2000 itérations sur JAVA Windows 8 x64, avec 20 itérations de warm-up.

En théorie

L'utilisation d'un doux comme spectateur bytecode nous pouvons traduire chaque opérateur en bytecode.

Dans le cadre 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);

Mesurer le nombre des instructions de bytecode sont utilisées par chaque opérateur, nous pourrions nous attendre instanceof et isInstance pour être plus rapide que IsAssignableFrom . Cependant, la performance réelle est pas déterminée par le bytecode, mais par le code de la machine (qui est dépendant de la plateforme). Faisons un micro référence pour chacun des opérateurs.

L'indice de référence

Crédit: Comme conseillé par @ aleksandr-Dubinsky, et grâce à @yura pour fournir le code de base, voici une JMH référence (voir ce ):

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 (score est un certain nombre d'opérations dans une unité de temps , donc plus le score 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

  • la référence dépend JVM et la plate-forme. Comme il n'y a pas de différence significative entre chaque opération, il pourrait être possible d'obtenir un résultat différent (et peut-être un ordre différent!) Sur une autre version de JAVA et / ou des plates-formes comme Solaris, Mac ou Linux.
  • l'indice de référence compare les performances de « B est une instance de » quand « B étend 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 extends A), les résultats peuvent être différents.
  • il est généralement conseillé d'écrire le code premier choix l'un des opérateurs (le plus pratique) et le profil de votre code pour vérifier s'il y a un goulot d'étranglement. 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, dans le cadre instanceof de votre code pourrait plus facilement obtenir optimisé qu'un par exemple ... isInstance

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;

Merci à JIT, le code est optimisé à un moment donné et nous obtenons:

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

Remarque

A l'origine ce poste a fait son indice de référence en utilisant un pour boucle en JAVA brute, ce qui a donné des résultats peu fiables comme une optimisation comme Just In Time peut éliminer la boucle. Donc, il était surtout mesurer combien de temps le compilateur JIT prendre pour optimiser la boucle: voir Est-ce que l'opérateur instanceof générer beaucoup de frais généraux? Pourquoi?

  • Comment est instanceof mis en œuvre à l'intérieur JAVA?
  • Performance impact de l'utilisation instanceof en Java
  • Un équivalent plus direct à a instanceof B est

    B.class.isInstance(a)
    

    Cela fonctionne (fausses déclarations) quand est a trop null.

    En dehors des différences de base mentionnés ci-dessus, il existe une différence subtile entre noyau opérateur instanceof et un procédé IsAssignableFrom dans la classe.

    Lire comme instanceof « est-ce (la partie gauche) l'instance de cette sous-classe ou d'une de ces (la partie droite) » et lire comme x.getClass().isAssignableFrom(Y.class) « Puis-je écrire X x = new Y() ». En d'autres termes, instanceof opérateur vérifie si l'objet gauche est la même ou sous-classe de la classe à droite, tandis que isAssignableFrom vérifie si nous pouvons attribuer objet de la classe de paramètres (de) à la référence de la classe sur laquelle la méthode est appelée. < br> Notez que ces deux considèrent l'instance réelle et non le type de référence.

    Prenons un exemple de 3 classes A, B et C où C prolonge B et B se prolonge 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 peu importe ce que false X est

    null.getClass (). IsAssignableFrom (X) va lancer une NullPointerException

    Il y a encore une autre différence. Si le type (classe) pour effectuer le test est dynamique, par exemple passé en tant que paramètre de méthode, alors instanceof ne sera pas coupé 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 cette réponse est déjà couvert. Peut-être que cet exemple est utile à quelqu'un.

    Ce fil m'a donné un aperçu de la façon dont différait de instanceof isAssignableFrom, donc je pensais que je partage quelque chose de moi-même.

    Je l'ai trouvé que l'utilisation d'être le <=> ne (probablement pas le seul, mais peut-être le plus facile) de demander soi-même si une référence d'une classe peut prendre des instances d'une autre, quand on a des cas de ne classe faire la comparaison.

    Par conséquent, je ne trouve pas l'aide de l'opérateur de comparer cessibilité <=> être une bonne idée quand j'avais étaient des cours, à moins que je contemplais créer une instance de l'une des classes; Je pensais que ce serait bâclée.

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

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

    ou

    ... obj instanceof A ...

    Mais la solution de IsAssignableFrom exige que le type de obj soit visible ici. Si ce n'est pas le cas (par exemple, le type de obj peut-être d'une classe intérieure privée), cette option est hors. Cependant, la solution instanceof serait toujours travailler.

    instanceof ne peut pas être utilisé avec des types primitifs ou types génériques soit. 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 vérifier le type contre paramètre T. Utilisez est l'effacement objet au lieu depuis plus d'informations de type générique sera effacé lors de l'exécution

    .

    ne compile pas en raison d'un effacement de type suppression de la référence d'exécution. Cependant, le code ci-dessous la compilation:

    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 code pseudo ci-dessus est une définition, si les références de type / classe A est assignable des références de type / classe B. Il est une définition récursive. Pour certains, il peut être utile, pour d'autres, il peut être source de confusion. Je l'ajoute dans cas où quelqu'un devrait trouver utile. Ceci est juste une tentative de saisir ma compréhension, il n'est pas la définition officielle. Il est utilisé dans une certaine implémentation Java VM et travaille pour de nombreux exemples de programmes, et bien que je ne peux pas guarentee qu'il capte tous les aspects de IsAssignableFrom, ce n'est pas complètement.

    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
    

    Alors que nous pouvons conclure: instanceof aussi vite que isInstance () et IsAssignableFrom () non loin (+ 0,9% du temps de executon ). Donc, pas de différence réelle que vous choisissez

    quelques tests que nous avons fait dans notre show d'équipe qui fonctionne plus rapidement que A.class.isAssignableFrom(B.getClass()) B instanceof A. cela peut être très utile si vous avez besoin de vérifier cela sur un grand nombre d'éléments.

    Licencié sous: CC-BY-SA avec attribution
    Non affilié à StackOverflow
    scroll top