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:
- Para usar isInstance , necesitamos saber cuál es una de las clases en tiempo de compilación.
- 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 .
- 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 .
- 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.