12 de agosto de 2007

Introspección (java.lang.reflect)

A partir del JDK1.1.x se incorporó a Java la posibilidad de introspectar clases. Esta característica fue fundamental para el posterior desarrollo de herramientas de desarrollo (valga la redundancia).

Basicamente, la idea es: Dado el nombre de una clase poder obtener una instancia invocando alguno de sus constructores. Luego, dado el nombre de un método, poderlo invocar, y así.


Obtener una Instancia dado el Nombre de la Clase

El caso más simple es invocar al constructor nulo (el que no recibe argumentos).

// defino el nombre de clase
String sClass = "java.awt.Frame";

// obtengo una instancia de Class para esa clase
Class clazz = Class.forName(sClass);

// obtengo la instancia a traves del ctor nulo
Object obj = clazz.newInstance();

Pero probablemente querramos instanciar la clase utilizando otro constructor, que reciba algún parámetro. Por ejemplo: el constructor que recibe un String.


Obtener un Método o un Constructor para luego invocarlo

Los métodos se identifican por su nombre y por un Class[] que indica el tipo y el orden de los argumentos que el método (o constructor) espera recibir.

Por ejemplo el método buscar(Connection con, int empno) se identifica así:

String nombre = "buscar";
Class[] args = {Connection.class, Integer.TYPE};

Notemos que el nómbre del método no es suficiente para identificarlo. Como el método puede estar sobrecargado necesitamos tambíen el Class[] para describir sus argumentos.

En el caso de los constructores no es necesario definir su nombre. Solo se necesita el Class[].

Luego, al momento de invocarlo necesitamos un Object[] con los valores de los parámetros que le vamos a pasar al método o constructor.

// obtengo una instancia de Class para esa clase
Class clazz = Class.forName(sClass);

// constructor que vamos a utilizar
Class cArgs[] = { String.class };
Object oParams[] = { new String("Demo Reflection") };

// obtengo el constructor que recibe (en este caso) un String
Constructor ctor = clazz.getConstructor(cArgs);

// invoco el constructor pasandole los argumentos que espera
Object obj = ctor.newInstance(oParams);


Invocar un Método sobre un Objeto

Ahora vamos a invocarle un método al objeto obj. El método setSize(int,int). Definimos el Class[] y el Object[] con los valores de los argumentos.

Recordemos que ya tenemos los objetos clazz (Class apuntando a java.awt.Frame) y obj (la instancia que obtuvimos luego de invocar al constructor que recibe un String).

// defino el nombre del metodo
String sMetodo1 = "setSize";

// defino el Class[] con el que voy a indicar que el metodo que espero
// obtener recibe dos parametros de tipo int
Class cArgs1[] = { Integer.TYPE,Integer.TYPE };

// defino un array de argumentos. Al metodo le voy a pasar
// los
argumentos: 300,300
Object oParams1[] = { new Integer(300), new Integer(300) };

// obtengo el metodo identificado por su nombre y un Class[]
Method mtd1 = clazz.getMethod(sMetodo1,cArgs1);

// lo invoco sobre el objeto obj
mtd1.invoke(obj,oParams1);


El ejemplo que veremos a continuación, que es equivalente a este segmento de código:

Frame f=new Frame("Demo Reflection");
f.setSize(300,300);
f.setVisible(true);

TestReflection.java


El resultado es: