Reflexión: Alterando los objetos de forma dinámica

Publicado en

De acuerdo con la Real Academia de la Lengua Española, reflexión es la “acción y efecto de reflexionar”, en tanto que reflexionar es: “considerar nueva o detenidamente algo”.

Por otro lado, Wikipedia describe la reflexión en informática como “la capacidad que tiene un programa de ordenador para observar y opcionalmente modificar su estructura de alto nivel”.

La reflexión en la programación de software es la característica de un lenguaje que permite conocer su estructura de manera dinámica (en tiempo de ejecución).

Ejemplo
El listado 1 muestra el código de un programa en java que tiene la siguiente funcionalidad:

  1. Crear una clase del tipo que se proporcione en el primer parámetro.
  2. Crear un objeto a partir de la clase creada en el punto anterior.
  3. Obtener el valor de la propiedad indicada en el segundo parámetro.
  4. Modificar el valor de la propiedad que se obtuvo en el punto anterior con el valor del tercer parámetro.
package org.josuemb;
import java.lang.reflect.Field;

public class EjemploReflexion
{

   @SuppressWarnings(“unchecked”) // Evita un warning al compilar

  public static void main(String[] args) {

    try {
      if (args.length < 3)
      {
        System.out.println(“Error en llamado\n”);
        System.out.println(“Sintaxis:”);
        System.out.println(“\tjava EjemploReflexion NombreClase NombrePropiedad Valor”);
        System.exit(1);
      }

      System.out.println(“Creando clase [“+args[0]+”] de manera dinamica...”);
      Class clase = Class.forName(args[0]);

      System.out.println(“Creando objeto de tipo [“+clase.getName()+”] de manera dinamica...”);
      Object objeto = clase.newInstance();

      System.out.println(“Obteniendo propiedad [“+args[1]+”] del nuevo objeto de manera dinamica...”);
      Field propiedad = objeto.getClass().getDeclaredField(args[1]);

      // Es necesario establecer esta propiedad para acceder a los campos
      // de la clase directamente.
      propiedad.setAccessible(true);

      // Obtiene el valor de la propiedad
      Object valor1 = propiedad.get(objeto);

      // Imprime el valor de la propiedad
      System.out.println(propiedad.getName() + “=” + valor1);

      System.out.println(“Modificando valor de propiedad [“+propiedad.getName()+”]...”);

      //Establece el nuevo valor a la propiedad
      propiedad.set(objeto, args[2]);

      // Obtiene el valor de la propiedad
      Object valor2 = propiedad.get(objeto);

      // Imprime el valor de la propiedad
      System.out.println(propiedad.getName()+” = “+valor2);

      System.exit(0);

    } catch (Exception e) {
      e.printStackTrace();
    } // catch
  } // main
} // EjemploReflexion

Listado 1. EjemploReflexion.java.

El ejemplo EjemploReflexion funciona con cualquier clase. La única restricción (por sencillez en la programación) es que la propiedad a modificar sea de tipo java.lang.String.

Para simplificar aún más la prueba se proporciona también la clase Cliente. Esta se muestra en el listado 2.

package org.josuemb;

public class Cliente {

  private String nombre;
  private String lugar;

  public String getNombre()
  {
    return nombre;
  }

  public void setNombre(String nombre)
  {
    this.nombre = nombre;
  }

  public String getLugar()
  {
    return lugar;
  }

  public void setLugar(String lugar)
  {
    this.lugar = lugar;
  }

}

Listado 2. Cliente.java.

Para ejecutar el ejemplo solo es necesario compilar ambas clases y ejecutarlas mediante el comando:

java EjemploReflexion org.josuemb.Cliente nombre Josue

En caso de que se desee ejecutar el ejemplo con otra clase, la sintaxis sería:

java EjemploReflexion [NombreClase] [NombrePropiedad] [Valor]

El resultado de la ejecución es el siguiente:

$ java org.josuemb.EjemploReflexion org.josuemb.Cliente nombre Josue
Creando clase [org.josuemb.Cliente] de manera dinamica...
Creando objeto de tipo [org.josuemb.Cliente] de manera dinámica...
Obteniendo propiedad [nombre] del nuevo objeto de manera dinamica...
nombre=null
Modificando valor de propiedad [nombre]...
nombre=Josue

Versión Groovy

A continuación muestro el mismo ejemplo en el lenguaje Groovy. El concepto es el mismo, simplemente que las características de Groovy permiten que la sintaxis sea más sencilla.

  if(args.size() < 3 ) {
  println “””Error en llamado

  Sintaxis:
  groovy EjemploReflexion.groovy NombreClase NombrePropiedad Valor”””
  System.exit 1
  }

  println “Creando clase [${args[0]}] de manera dinamica...”

  def clase = args[0] as Class;

  println “Creando objeto de tipo [$clase.name] de manera dinamica...”
  def objeto = clase.newInstance()

  println “Obteniendo propiedad [${args[1]}] del nuevo objeto de manera dinamica...

  println “${args[1]}=${objeto[args[1]]}”

  //Establece el nuevo valor a la propiedad
  objeto[args[1]] = args[2]

  println “Modificando valor de propiedad [${args[1]}]...”
  objeto[args[1]] = args[2]

  println “${args[1]}=${objeto[args[1]]}”

Listado 3. EjemploReflexion.groovy.

package org.josuemb

class Cliente{
  String nombre
  String lugar
}

Listado 4. Cliente.groovy.

Para ejecutar el ejemplo solo es necesario compilar la clase Cliente mediante el comando:

groovyc EjemploReflexion.groovy

Y después ejecutar el Script EjemploReflexion mediante el comando:

groovy EjemploReflexion.groovy org.josuemb.Cliente nombre Josue

En caso de que se desee ejecutar el ejemplo con otra clase, la sintaxis sería:

groovy EjemploReflexion.groovy [NombreClase] [nombrePropiedad] [Valor]

El resultado será equivalente al del ejemplo con java.

Conclusión

La reflexión es una capacidad muy útil que proveen la mayoría de los lenguajes de programación modernos como Java, Groovy, Python, Ruby, C#, entre otros. Nos permite conocer la estructura interna de una clase, sus propiedades, constructores, métodos (parámetros de los mismos), ejecutar los métodos, e incluso obtener y modificar información de miembros privados de una clase. Todo esto sin necesidad de tener el código fuente ni la documentación de una clase. También por medio de la reflexión podemos lograr que un programa pueda adaptarse dinámicamente a distintas situaciones, en tiempo de ejecución.

Referencias

  1. Reflexión (informática), Wikipedia.
  2. G. McCluskey. Using Java Reflection, Sun Developer Network.
  3. D. Sosnoski. Java Programming dynamics”, IBM developerWorks. h

 

Bio

Josué Martínez Buenrrostro (@josuemb) es Sr. Software Architect Java en Quarksoft. Estudió Ing. en Comunicaciones y Electrónica en el IPN y ha desarrollado software profesionalmente por más de 14 años. Es un usuario y promotor activo del Software Libre (FLOSS), y considera que la industria del software puede ser un motor de desarrollo para México con un alto potencial que aún no ha sido explotado.