Saltar al contenido
Codifíca.me | Desarrollo web | Programación

Reflexion en Java

25 agosto, 2010

En este artículo explicaremos como funciona la reflexion en java.

Imagina que en función de un parámetro tuvieses que ejecutar una clase u otra, lo primero que se nos viene a la cabeza es hacer un IF-ELSE tantas veces como valores diferentes pueda tener el parametro y dentro de cada IF crear una instancia de la clase correspondiente, si por ejemplo el parámetro pudiese tomar 100 valores diferentes quedaría un IF inmenso y poco legible. Ejemplo1 (con 5 tareas usando IF-ELSE):

CLASE PPAL (testReflexion)

import tareas.Tarea1;
import tareas.Tarea2;
import tareas.Tarea3;
import tareas.Tarea4;
import tareas.Tarea5;

public class testReflexion {

 public static void main(String[] args)
 {
  String tarea1 = “Tarea1″;
  String tarea2 = “Tarea2″;
  String tarea3 = “Tarea3″;
  String tarea4 = “Tarea4″;
  String tarea5 = “Tarea5″;
  
  //En este caso el supuesto parametro de entrada seria–> “Tarea4″
  String param = tarea4;
  
  if (param.compareTo(tarea1)==0)
  {
   Tarea1 t = new Tarea1();
   t.ejecutarTarea();
  }
  else if (param.compareTo(tarea2)==0)
  {
   Tarea2 t = new Tarea2();
   t.ejecutarTarea();
  }
  else if (param.compareTo(tarea3)==0)
  {
   Tarea3 t = new Tarea3();
   t.ejecutarTarea();
  }
  else if (param.compareTo(tarea4)==0)
  {
   Tarea4 t = new Tarea4();
   t.ejecutarTarea();
  }
  else if (param.compareTo(tarea5)==0)
  {
   Tarea5 t = new Tarea5();
   t.ejecutarTarea();
  }
  //este else if sera tan grande como tareas diferentes se puedan ejecutar
 }

}

 CLASE TAREA (todas son iguales, sólo cambia el mensaje) 

package tareas;

public class Tarea4 {
 
 String mensaje = “”;
 
 public Tarea4()
 {
  this.setMensaje(”Se ha ejecutado la tarea 4″);
 }

 public void ejecutarTarea()
 {
  System.out.println(mensaje);
 }

 public String getMensaje() {
  return mensaje;
 }

 public void setMensaje(String mensaje) {
  this.mensaje = mensaje;
 }
 
}

 

Java dispone de un mecanismo denominado reflexión por el cual a traves del nombre de una clase podemos obtener un objeto de dicha clase, crear una instancia de ella, obtener todas sus propiedades (publicas), obtener todos sus métodos (publicos)…

 Utilizando reflexión, si el parámetro que nos llega es el nombre de la clase, podemos obtener una instancia de la clase directamente, del mismo modo nos podrían pasar el método que queremos ejecutar, incluso los parámetros de entrada para ese método. Ejemplo2(usando Reflexion):

CLASE PPAL (testReflexion2)

import java.lang.reflect.*;

public class testReflexion2 {
 public static void main(String[] args)
 {
  String tarea1 = “Tarea1″;
  String tarea2 = “Tarea2″;
  String tarea3 = “Tarea3″;
  String tarea4 = “Tarea4″;
  String tarea5 = “Tarea5″;
  
  //En este caso el supuesto parametro de entrada seria–> “Tarea4″
  String param = tarea4;
  
  Class clase;
  Object objeto;
  Method ejecutarTarea;
  
  try
  {
   // Cargamos la clase a partir del nombre
   //la ruta tienen que ser completa
   clase = Class.forName(”tareas.”+tarea4);
   try
   {
    // creamos la instancia de la clase
    objeto = clase.newInstance();
    
    try
    {
     //obtenemos el metodo
     ejecutarTarea = clase.getMethod(”ejecutarTarea”, null);
     //ejecutamos el método
     ejecutarTarea.invoke(objeto, null);
    }
    catch (NoSuchMethodException e)
{
     System.out.println(”Error al acceder al metodo. ” + e);
            } catch (InvocationTargetException e)
{
                 System.out.println(”Error al ejecutar el metodo. ” + e);
           }
   }
   catch (IllegalAccessException e)
{
    System.out.println(”Error al instanciar el objeto. ” + e);   
   }
   catch (InstantiationException e)
{
            System.out.println(”No se ha encontrado la clase. ” + e);
       }
      }
  catch (ClassNotFoundException e)
{
   System.out.println(”Error al instanciar el objeto. ” + e);   
  }
 }
}

Podríamos delegar la creación de nuevos objetos a una clase factoria, de esta manera se hace transparente la creación de los objetos al programa principal.Ejemplo 3 (Usando Reflexión, Fatoria e Interface):

CLASE PPAL (testReflexion3)

import tareas.InterfaceTarea;
import factoria.Factoria;

public class testReflexion3 {
 public static void main(String[] args)
 {
  String tarea1 = “Tarea1F”;
  String tarea2 = “Tarea2F”;
  String tarea3 = “Tarea3F”;
  String tarea4 = “Tarea4F”;
  String tarea5 = “Tarea5F”;
  
  //En este caso el supuesto parametro de entrada seria–> “Tarea1F”
  String param = tarea1;
  Factoria f = new Factoria();
  InterfaceTarea it = f.getTarea(param);
  it.ejecutarTarea();
  
 }
}

CLASE FACTORIA

package factoria;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import tareas.InterfaceTarea;

public class Factoria {

 public static InterfaceTarea getTarea( String tarea ) {
  InterfaceTarea objeto = null;
  Class clase = null;
  try
  {
   clase = Class.forName(”tareas.”+tarea);
   try
   {
    objeto = (InterfaceTarea)clase.newInstance();
   }
   catch (InstantiationException e) {
          System.out.println(”No se ha encontrado la clase. ” + e);
      }
   catch (IllegalAccessException e) {
    System.out.println(”Error al instanciar el objeto. ” + e);   
   }
  }
  catch (ClassNotFoundException e) {
   System.out.println(”Error al instanciar el objeto. ” + e);   
  }
  return objeto;
 }
}

CLASE INTERFACETAREA

package tareas;

public interface InterfaceTarea {
 void ejecutarTarea();
}

CLASE TAREA (adaptada a usar el interface)

package tareas;

public class Tarea1F implements InterfaceTarea{
 
 String mensaje = “”;
 
 public Tarea1F()
 {
  this.setMensaje(”Se ha ejecutado la tarea 1″);
 }

 public void ejecutarTarea()
 {
  System.out.println(mensaje);
 }

 public String getMensaje() {
  return mensaje;
 }

 public void setMensaje(String mensaje) {
  this.mensaje = mensaje;
 }
}

Principales métodos de la API Reflection de JAVA:

java.lang.Class forName(String className): Carga una clase del classpath a partir de su nombre (nombre completo, con todos los paquetes. Si la clase no se puede cargar, porque no se encuentra en el classpath, se lanzará una java.lang.ClassNotFoundException.

java.lang.reflect.Field getField(String name): Devuelve un campo público de la clase, a partir de su nombre. Si la clase no contiene ningún campo con ese nombre, se comprueban sus superclases recursivamente, y en caso de no encontrar finalmente el campo, se lanzará la excepcion java.lang.NoSuchFieldException.

java.lang.reflect.Field[] getFields(): Devuelve un array con todos los campos públicos de la clase, y de sus superclases.

java.lang.reflect.Method getMethod(String name, Class[] parameterTypes): Devuelve un método público de la clase, a partir de su nombre, y de un array con las clases de los parámetros del método. Si la clase no contiene ningún método con ese nombre y esos parámtetros, se lanzará la excepcion java.lang.NoSuchMethodException.

java.lang.reflect.Method[] getMethods(): Devuelve un array con todos los métodos públicos de la clase, y de sus superclases.

java.lang.Class[] getInterfaces(): Devuelve un array con todos los interfaces que implementa la clase.

public String getName(): Devuelve el nombre del campo.

public Class getType(): Devuelve la clase del campo.

public Object get(Object obj): Devuelve el valor del campo en un objeto.

public void set(Object obj, Object value): Asigna un valor al campo en un objeto.

public String getName(): Devuelve el nombre del método.

public Class[] getParameterTypes(): Devuelve un array con las clases de los parámetros del método.

public Class[] getExceptionTypes(): Devuelve un array con las clases de las excepciones que puede lanzar el método.

public Class getReturnType(): Devuelve la clase del valor que devuelve el método.

public Object invoke(Object obj, Object[] args): Ejecuta el método sobre un objeto, pasándole los parámetros necesarios, y devuelve su resultado.