Class.isInstance vs Class.isAssignableFrom

1. Introducción

En este tutorial rápido, veremos la diferencia entre Class.isInstance y Class.isAssignableFrom . Aprenderemos a usar cada método y cuáles son las diferencias entre ellos.

2. Configuración

Configuremos una interfaz y un par de clases para usar mientras exploramos las funciones Class.isInstance y Class.isAssignableFrom .

Primero, definamos una interfaz:

public interface Shape { }

A continuación, definamos una clase que implemente Shape :

public class Triangle implements Shape { }

Ahora, crearemos una clase que amplíe Triangle :

public class IsoscelesTriangle extends Triangle { }

3. Class.isInstance

El método isInstance en la clase Class es equivalente al operador instanceof . El método isInstance se introdujo en Java 1.1 porque se puede utilizar de forma dinámica. Este método devolverá verdadero si el argumento no es nulo y se puede convertir correctamente al tipo de referencia sin generar una ClassCastException .

Veamos cómo podemos usar el método isInstance con la interfaz y las clases que definimos:

@Test public void whenUsingIsInstanceProperly_desiredResultsHappen() { Shape shape = new Triangle(); Triangle triangle = new Triangle(); IsoscelesTriangle isoscelesTriangle = new IsoscelesTriangle(); Triangle isoscelesTriangle2 = new IsoscelesTriangle(); Shape nonspecificShape = null; assertTrue(Shape.class.isInstance(shape)); assertTrue(Shape.class.isInstance(triangle)); assertTrue(Shape.class.isInstance(isoscelesTriangle)); assertTrue(Shape.class.isInstance(isoscelesTriangle2)); assertFalse(Shape.class.isInstance(nonspecificShape)); assertTrue(Triangle.class.isInstance(shape)); assertTrue(Triangle.class.isInstance(triangle)); assertTrue(Triangle.class.isInstance(isoscelesTriangle)); assertTrue(Triangle.class.isInstance(isoscelesTriangle2)); assertFalse(IsoscelesTriangle.class.isInstance(shape)); assertFalse(IsoscelesTriangle.class.isInstance(triangle)); assertTrue(IsoscelesTriangle.class.isInstance(isoscelesTriangle)); assertTrue(IsoscelesTriangle.class.isInstance(isoscelesTriangle2)); }

Como podemos ver, el lado derecho puede ser equivalente o más específico que el lado izquierdo . Proporcionar un valor nulo al método isInstance devuelve falso .

4. Class.isAssignableFrom

El método Class.isAssignableFrom devolverá verdadero si la clase en el lado izquierdo de la declaración es la misma o es una superclase o superinterfaz del parámetro Class proporcionado .

Usemos nuestras clases con el método isAssignableFrom :

@Test public void whenUsingIsAssignableFromProperly_desiredResultsHappen() { Shape shape = new Triangle(); Triangle triangle = new Triangle(); IsoscelesTriangle isoscelesTriangle = new IsoscelesTriangle(); Triangle isoscelesTriangle2 = new IsoscelesTriangle(); assertFalse(shape.getClass().isAssignableFrom(Shape.class)); assertTrue(shape.getClass().isAssignableFrom(shape.getClass())); assertTrue(shape.getClass().isAssignableFrom(triangle.getClass())); assertTrue(shape.getClass().isAssignableFrom(isoscelesTriangle.getClass())); assertTrue(shape.getClass().isAssignableFrom(isoscelesTriangle2.getClass())); assertFalse(triangle.getClass().isAssignableFrom(Shape.class)); assertTrue(triangle.getClass().isAssignableFrom(shape.getClass())); assertTrue(triangle.getClass().isAssignableFrom(triangle.getClass())); assertTrue(triangle.getClass().isAssignableFrom(isoscelesTriangle.getClass())); assertTrue(triangle.getClass().isAssignableFrom(isoscelesTriangle2.getClass())); assertFalse(isoscelesTriangle.getClass().isAssignableFrom(Shape.class)); assertFalse(isoscelesTriangle.getClass().isAssignableFrom(shape.getClass())); assertFalse(isoscelesTriangle.getClass().isAssignableFrom(triangle.getClass())); assertTrue(isoscelesTriangle.getClass().isAssignableFrom(isoscelesTriangle.getClass())); assertTrue(isoscelesTriangle.getClass().isAssignableFrom(isoscelesTriangle2.getClass())); assertFalse(isoscelesTriangle2.getClass().isAssignableFrom(Shape.class)); assertFalse(isoscelesTriangle2.getClass().isAssignableFrom(shape.getClass())); assertFalse(isoscelesTriangle2.getClass().isAssignableFrom(triangle.getClass())); assertTrue(isoscelesTriangle2.getClass().isAssignableFrom(isoscelesTriangle.getClass())); assertTrue(isoscelesTriangle2.getClass().isAssignableFrom(isoscelesTriangle2.getClass())); }

Al igual que en el ejemplo de isInstance , podemos ver claramente que el lado derecho debe ser el mismo o más específico que el lado izquierdo. También podemos ver que nunca podemos asignar nuestra interfaz Shape .

5. Las diferencias

Ahora que hemos presentado un par de ejemplos detallados, repasemos algunas de las diferencias:

  1. Para usar isInstance , necesitamos saber cuál es una de las clases en tiempo de compilación.
  2. Solo se puede usar isAssignableFrom con primitivas. Debemos tener en cuenta que si estamos usando primitivas, el método sólo devolverá verdadero si la clase de objeto y clase de parámetros son exactamente la misma clase .
  3. El método isAssignableFrom arrojará una NullPointerException si el objeto utilizado en cualquier lado de la expresión es nulo . Si proporciona un valor nulo al método isInstance , devolverá falso .
  4. Deberíamos pensar en isInstance como una respuesta a la pregunta de si el objeto proporcionado es del tipo con el que lo estamos comparando (ya sea directamente o mediante interfaz o herencia). Podemos pensar en isAssignableFrom como una respuesta a la pregunta de si podemos definir la clase de parámetro como la clase de objeto. En otras palabras, ¿se compilará Triangle triangle = new IsoscelesTriange () ?

6. Conclusión

En este breve tutorial, analizamos los métodos Class.isInstance y Class.isAssignableFrom y exploramos su uso y en qué se diferencian.

Como siempre, el código de ejemplo para este tutorial se puede encontrar en GitHub.