2009-01-30 11 views
389

¿Cuál de los siguientes es mejor?¿Cuál es la diferencia entre instanceof y Class.isAssignableFrom (...)?

a instanceof B 

o

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

La única diferencia que yo sepa, es decir, cuando 'a' es nulo, los primeros resultados falsos, mientras que el segundo se produce una excepción. Aparte de eso, ¿siempre dan el mismo resultado?

+13

Para los registros, isInstance() es el método más conveniente para verificar si un objeto puede convertirse en un tipo de clase (para obtener más detalles, consulte: http://tshikatshikaaa.blogspot.nl /2012/07/java-instanceof-isassignablefrom-or.html) – JVerstry

Respuesta

421

Al utilizar instanceof, necesita saber la clase de B en tiempo de compilación. Al usar isAssignableFrom(), puede ser dinámico y cambiar durante el tiempo de ejecución.

+10

no lo entiendo - por favor explique en ** por qué ** no podemos escribir 'una instancia de Bref.getClass()'. ¿Cómo puede ser esta la respuesta aceptada con tan poca explicación (o su falta de ella)? –

+51

La sintaxis es 'una instancia de Bref' no' una instancia de Bref.class'. El segundo argumento para el operador instanceof es un nombre de clase, no una expresión que se resuelve en una instancia de objeto de clase. –

+1

sí "dinámico" es evidente :) Aparte del rendimiento, esta es una verdadera diferencia. – peterk

4

Considere la siguiente situación. Supongamos que usted quiere comprobar si el tipo A es una superclase de la clase de obj, se puede ir en cualquier

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

O

... obj instanceof Un ...

Pero la solución isAssignableFrom requiere que el tipo de obj sea visible aquí. Si este no es el caso (por ejemplo, el tipo de obj puede ser de una clase privada interna), esta opción está desactivada. Sin embargo, la solución instanceof siempre funcionaría.

+2

Eso no es verdad. Consulte el comentario de "Adam Rosenfield" http://stackoverflow.com/questions/496928/what-is-the-difference-between-instanceof-and-class-isassignablefrom/496968#496968 –

+1

¿Podría explicar "Eso no es cierto" ? El comentario al que se refiere no tiene nada que ver con el escenario en mi publicación. Tengo un código de prueba que respalda mi explicación. – algebra

+0

Si tiene una referencia no nula a una instancia de objeto ('obj' en este ejemplo) de _ cualquier tipo_, puede llamar al método público' getClass() 'para obtener los metadatos de reflexión para la clase implementadora. Esto es cierto incluso si ese tipo de clase de implementación no sería legalmente visible en esa ubicación en el momento de la compilación. Está bien en tiempo de ejecución porque, para que usted tenga la referencia 'obj', alguna ruta de código que finalmente _did_ tenga acceso legal a la clase creó una y le dio (¿se filtró?) A usted. –

-2

algunas pruebas que hicimos en nuestro equipo muestran que A.class.isAssignableFrom(B.getClass()) funciona más rápido que B instanceof A. esto puede ser muy útil si necesita verificar esto en una gran cantidad de elementos.

+13

Hm, si tiene un cuello de botella en 'instanceof', creo que tiene serios problemas de diseño ... – sleske

+0

La respuesta de JBE presenta una hipótesis que difiere de su hipótesis. –

23

Además de las diferencias básicas mencionadas anteriormente, existe una sutil diferencia entre el operador instanceof y el método isAssignableFrom en Class.

Leer instanceof como “es la siguiente (la parte izquierda) la instancia de este o cualquier subclase de esta (la parte derecha)” y leer x.getClass().isAssignableFrom(Y.class) como “¿Puedo escribir X x = new Y()”. En otras palabras, el operador instanceof comprueba si el objeto izquierdo es igual o la subclase de la clase derecha, mientras que isAssignableFrom comprueba si podemos asignar el objeto de la clase de parámetro (desde) a la referencia de la clase a la que se llama el método.
Tenga en cuenta que ambos consideran la instancia real no el tipo de referencia.

Considérese un ejemplo de 3 clases A, B y C, donde C se extiende B y B se extiende 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. 
+2

Lo entendió mal, vea este código http://pastebin.com/tfCFs0QY –

+3

'b instancia de A' es equivalente a' A.class.isAssignableFrom (b.getClass()) '(como notó OP). Tu ejemplo es correcto pero irrelevante. – Karu

+0

Dado que 'new Y()' puede no ser legal si 'Y' es abstracto o sin constructor público por defecto, puede decir' X x = (Y) null' es legal si y solo si 'x.getClass(). IsAssignableFrom (Y.class) 'es verdad. –

14

También hay otra diferencia:

nula instanceof X es false no importa qué X es

null.getClass().isAssignableFrom (X) lanzará una NullPointerException

+4

-1, incorrecto: 'instancia nula de X' (donde X es una clase conocida en tiempo de compilación) siempre devolverá' false'. – Caspar

+4

@ Caspar mientras está en lo correcto, la idea básica fue un buen punto. Edité la publicación para que sea correcta. – erickson

+1

esto es útil, la caja de borde siempre es importante :). – trillions

9

Hay otra diferencia. Si el tipo (Clase) para probar en contra es dinámico, p. pasado como un parámetro de método, entonces instanceof no lo cortará por usted.

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

pero se puede hacer:

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

Vaya, veo que esta respuesta ya está cubierto. Tal vez este ejemplo sea útil para alguien.

+2

en realidad ninguna respuesta es realmente correcta isAssignableFrom trabajo w/classes, Class.isInstance es el análogo de * 'instanceof' * – bestsss

33

Un equivalente más directa a a instanceof B es

B.class.isInstance(a) 

Esto funciona (devuelve false) cuando a es null también.

7

Este hilo me proporcionó cierta información sobre cómo instanceof difería de isAssignableFrom, así que pensé en compartir algo mío.

He encontrado que el uso de isAssignableFrom es la única (probablemente no la única, pero posiblemente la más fácil) forma de preguntarme si una referencia de una clase puede tomar instancias de otra, cuando una tiene instancias de ninguna de las clases a hacer la comparación

Por lo tanto, no pude utilizar el operador instanceof para comparar la asignabilidad para que fuera una buena idea cuando todo lo que tenía eran clases, a menos que contemplara crear una instancia de una de las clases; Pensé que esto sería descuidado.

87

Hablando en términos de rendimiento:

TL; DR

Uso isInstance o instanceof que tienen un rendimiento similar. isAssignableFrom es un poco más lento.

Ordenado por rendimiento:

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

Basado en un punto de referencia de 2000 iteraciones en JAVA 8 Windows x64, con 20 iteraciones de calentamiento.

En teoría

El uso de un suave como bytecode viewer podemos traducir cada operador en bytecode.

En el contexto 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); 

medición cuántas instrucciones de código de bytes es utilizado por cada operador, se puede esperar instanceof y isInstance a ser más rápido que isAssignableFrom. Sin embargo, el rendimiento real NO está determinado por el bytecode, sino por el código máquina (que depende de la plataforma). Hagamos un micro benchmark para cada uno de los operadores.

El punto de referencia

Crédito: según las indicaciones de Aleksandr @-Dubinsky, y gracias a @yura para proporcionar el código base, aquí hay una JMH de referencia (ver este tuning guide):

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

Dio los siguientes resultados (la puntuación es una serie de operaciones en una unidad de tiempo, por lo que cuanto mayor sea la puntuación, mejor):

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 

Advertencia

  • el punto de referencia es JVM y dependiente de la plataforma. Como no hay diferencias significativas entre cada operación, es posible obtener un resultado diferente (¡y tal vez diferente!) En una versión y/o plataforma JAVA diferente como Solaris, Mac o Linux.
  • el índice de referencia compara el rendimiento de "es B una instancia de A" cuando "B extiende A" directamente. Si la jerarquía de clases es más profunda y más compleja (como B extiende X que se extiende Y que se extiende Z que se extiende A), los resultados pueden ser diferentes.
  • generalmente se aconseja escribir el código primero seleccionando uno de los operadores (el más conveniente) y luego perfilar su código para verificar si hay un cuello de botella de rendimiento. Tal vez este operador es insignificante en el contexto de su código, o tal vez ...
  • en relación con el punto anterior, instanceof en el contexto de su código podría optimizarse más fácilmente que un isInstance por ejemplo ...

Para dar un ejemplo, tomar el siguiente bucle:

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; 

Gracias al JIT, el código está optimizado en algún momento y se obtiene:

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

Nota

Originalmente este post estaba haciendo su propio punto de referencia usando un bucle de en JAVA prima, que dio resultados poco fiables ya que algunos de optimización como Just In Time puede eliminar el bucle. Por lo que era en su mayoría mide el tiempo que hizo el compilador JIT tomar para optimizar el bucle: ver Performance test independent of the number of iterations para más detalles

preguntas relacionadas

+6

Sí, 'instanceof' es un bytecode que utiliza esencialmente la misma lógica que' checkcast' (el bytecode detrás del casting) . Será inherentemente más rápido que las otras opciones, independientemente del grado de optimización de JITC. –

+5

¡No escriba sus propias microbenchmarks! Usa JMH. –

+1

Lo cual tiene sentido, ya que 'isAssignableFrom()' es dinámico. – Matthieu

3

instanceof no se puede usar con tipos primitivos o gener ic tipos tampoco. Al igual que en el siguiente código:

//Define Class<T> type ... 

Object e = new Object(); 

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

El error es: No se puede realizar instanceof comprobar contra el tipo de parámetro T. Use es objeto borrado vez desde más información de tipo genérico se borrará en tiempo de ejecución.

No se compila debido al borrado de tipo que elimina la referencia de tiempo de ejecución. Sin embargo, el código de abajo compilará:

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

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

El pseudo código anterior es una definición de, si las referencias de tipo/clase A es asignable a partir de referencias de tipo/clase B. Es una definición recursiva. Para algunos puede ser útil, para otros puede ser confuso. Lo agrego en caso de que alguien lo encuentre útil. Esto es solo un intento de capturar mi entendimiento, no es la definición oficial. Se utiliza en una determinada implementación de Java VM y funciona para muchos programas de ejemplo, así que aunque no puedo garantizar que capture todos los aspectos de isAssignableFrom, no está completamente desactivado.

+2

Explique qué hace este código y cómo responde la pregunta. –

0

Hablando en términos de rendimiento "2" (con 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(); 
} 
} 

Da:

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 

Así que podemos concluir: instanceof tan rápido como isInstance() y isAssignableFrom() no muy lejos (+ 0,9% de tiempo de ejecución).Entonces, no hay diferencia real, elija lo que elija

Cuestiones relacionadas