Ardilla Quio Ardilla Quio

26 de Abril de 2011

Java Reflection (parte 1)

Una de las funcionalidades más potentes y poco conocidas de Java es su soporte para reflexión. Mediante la Java Reflection API el programador puede inspeccionar y manipular clases e interfaces (así como sus métodos y campos) en tiempo de ejecución, sin conocer a priori (en tiempo de compilación) los tipos y/o nombres de las clases específicas con las que está trabajando.

Quizás pueda parecernos en una primera impresión una funcionalidad con usos limitados. Pero debemos saber que, por ejemplo, muchos frameworks de alto nivel como Hibernate, Spring o Tapestry hacen un uso extensivo de esta API para facilitarle la vida al programador al permitirle que use simples clases POJO para trabajar con ellas. Otros frameworks menos potentes (o versiones antiguas de estos mismos frameworks), obligaban al programador a que sus clases implementaran ciertos interfaces o pertenecieran a complicadas jerarquías de clases, lo cual limitaba la flexibilidad del programador y complicaba la comprensión del código.

Clase

Cuando en nuestro programa queremos usar reflexión para poder trabajar con un objeto del que desconocemos su tipo (en el caso de Java, esto es lo mismo que decir que desconocemos el nombre de su clase), lo primero que debemos hacer es averiguar la clase a la que pertenece. En situaciones normales conoceremos el tipo de un objeto en tiempo de compilación, con lo que obtendríamos su clase de la forma:

Class userClass = User.class;

En aquellas situaciones en las que no conozcamos el nombre de una clase en tiempo de compilación, podemos obtener su clase en tiempo de ejecución a partir de un String que contenga el nombre completo de la clase (incluyendo los paquetes, lo que se conoce como nombre de clase totalmente calificado) usando la función Class.forName():

Class userClass = Class.forName("com.test.model.AdminUser");

En nuestro ejemplo, la clase es AdminUser que pertenece al paquete com.test.model.

Si por algún motivo la clase no existiese, se lanzaría una excepción ClassNotFoundException para indicarlo.

En sentido inverso, teniendo la clase de un objeto también podremos obtener una cadena con el nombre de la clase:

String className = userClass.getName();

Este ejemplo nos devuelve el nombre totalmente calificado de la clase. Si queremos sólo el nombre simple (sin el paquete), debemos usar el método getSimpleName().

Información adicional

Podemos obtener el paquete de una clase mediante:

Package userPackage = userClass.getPackage();

Nótese que no devuelve un simple String con el nombre del paquete, sino un objeto de tipo Package que contiene información sobre el paquete y métodos para su manipulación.

También podemos acceder a la superclase de una clase (que será nuevamente un objeto Class, de forma que podemos seguir haciendo reflexión sobre él) mediante el método:

Class userSuperclass = userClass.getSuperclass();

Podemos obtener una lista de los interfaces que implementa una clase (en forma de array de objetos Class) mediante:

Class[] userInterfaces = userClass.getInterfaces();

Debe tenerse en cuenta que sólo se incluyen los interfaces implementados directamente por esta clase, no los que se heredan porque son implementados por alguna superclase de la jerarquía.

Constructores

Podemos acceder a la lista de todos los constructores de una clase mediante el método:

Constructor[] userConstructors = userClass.getConstructors();

Si conocemos los tipos de los parámetros de un constructor en particular, podemos acceder a ese constructor concreto sin tener que recorrer toda la lista. Esto se hace mediante el método getConstructor(), que admite como parámetros un array con los tipos específicos de ese constructor (en el mismo orden en el que están declarados). Por ejemplo, si sabemos que nuestra clase de ejemplo contiene un constructor que acepta los parámetros (String, String, Integer) podemos acceder a él mediante:

Constructor userConstructor = userClass.getConstructor(new Class[] {String.class, String.class, Integer.class});

Si no existiera un constructor con esos parámetros, se lanzaría una excepción de tipo NoSuchMethodException.

A la inversa, para un constructor también podemos obtener sus parámetros mediante:

Class[] params = userConstructor.getParameterTypes();

Una vez hallamos conseguido el constructor deseado, podemos instanciar un objeto mediante el método newInstance() con los parámetros adecuados. En nuestro ejemplo:

Constructor userConstructor = userClass.getConstructor(new Class[] {String.class, String.class, Integer.class});
User user = (User) userConstructor.newInstance("Antonio González", "agonzalez@mimail.com", 12);

Puedes leer más sobre este tema en nuestro artículo Java Reflection (parte 2).

17 comentarios

Carlos Arturo

25/04/2016 23:21:56

Excelente tutorial...Esta parte es importante para para la busqueda de clases y objetos...Muchas Gracias.

mario

17/05/2015 23:14:06

Muchisimas gracias por tu tutorial, desconocia esta parte de Java de como implementarla, y en este momento me preguntaba como hacer algo así.

Me haz ilustrado bastante y ayudado.

Mil gracias.

zapatao

19/11/2014 19:26:49

hola, gracias por tu aporte me sirvio de mucho.

Quisiera hacer una pregunta : si yo se el nombre de una clase que esta en otro paquete y no deseo poner toda la ruta(package) cuando lo invoque con el Class.forName, hay alguna funcion que me devuelva un package generico que busque en todos los paquetes hasta encontrar la clase??.

Luis Fernando Ortiz Vera

28/08/2014 04:31:19

Excelente aporte, muchas gracias!!

Francisco Cruz

27/07/2014 19:35:04

Excelente tutorial. la forma de explicarlo es muy concreta.

Luis

22/01/2014 11:32:59

Hola E.

Algunos ejemplos son:

- Aplicaciones de "debug" de otras aplicaciones (de las cuales desconoces sus clases).

- Aplicaciones de generación de documentación de código.

- Aplicaciones para generar tablas en base de datos a través de las clases existentes.

Un saludo.

E

21/01/2014 15:39:13

Hola, muy buena explicación, pero, ¿Me podrías dar algún ejemplo en dónde se utiliza ? O sea, me explico mejor, en que casos desconocería a que tipo de clase pertenece mi objeto.

Gracias

Anónimo

04/09/2013 00:16:30

Graxias, excelente aporte

Anónimo

02/06/2013 03:26:14

Este blog es hermoso, gracias.

Luis

15/03/2013 11:57:01

Hola osa.

La diferencia es que getConstructors devuelve sólo los constructores públicos mientras que getDeclaredConstructors devuelve todos los constructores: públicos, privados, ...

Puedes ver más en este enlace: http://stackoverflow.com/questions/8249173/what-is-the-di...

osa

14/03/2013 20:29:14

Y que diferencia existe entre getConstructors y getDeclaredConstructors me gustaria saberlo gracias.

Anónimo

11/01/2013 15:52:52

Buena explicación

donKsper

20/11/2012 21:38:22

Excelente aporte... muchas gracias!

Anónimo

04/10/2012 21:30:25

Amigos tengo una pregunta mm aver si me ayudan un poco tengo una clase:

public class grid_marca{

public static DefaultTableModel cont_table;

public static JPanel grid_marca(){

//aqui el cuerpo de la tabla

cont_table..................

}

}

luego tengo una segunda clase:

public class Grid {

public void grid(){

grid_marca objeto=new grid_marca()

objeto.cont_table.addRow(fila);

}

}

lo que quiero es que el "grid_marca" sea dinamico(por ejemplo quiero poner grid_venta u otro) y puedo agregar los datos a la tabla de esta segunda clase.

Le agradeciria mucho su respuesta.

---> eso no es este tema.......

Anónimo

09/12/2011 05:30:43

Amigos tengo una pregunta mm aver si me ayudan un poco tengo una clase:

public class grid_marca{

public static DefaultTableModel cont_table;

public static JPanel grid_marca(){

//aqui el cuerpo de la tabla

cont_table..................

}

}

luego tengo una segunda clase:

public class Grid {

public void grid(){

grid_marca objeto=new grid_marca()

objeto.cont_table.addRow(fila);

}

}

lo que quiero es que el "grid_marca" sea dinamico(por ejemplo quiero poner grid_venta u otro) y puedo agregar los datos a la tabla de esta segunda clase.

Le agradeciria mucho su respuesta.

Anónimo

04/12/2011 05:12:47

muy bien explicado+

Anónimo

23/10/2011 21:23:09

Buena descripcion :-)

Comentario anónimo
Comentar como usuario