El concepto Java Reflection y como utilizarlo

Java Reflection es quizás el API que más versatilidad aporta al lenguaje Java ya que nos permite resolver muchos problemas de una forma totalmente diferente a la habitual. El API de Java reflection nos permite leer los metadatos de nuestras clases y trabajar con ellos. En un principio no es nada sencillo de entender .Vamos a construir un ejemplo que nos clarifique las ideas. Imaginemos que tenemos varias clases que identifican tipos de productos. En nuestro caso pueden ser Ordenador y Lavadora.


package com.arquitecturajava;

public class Ordenador {

private String id;
private String descripcion;
private int potencia;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
public int getPotencia() {
return potencia;
}
public void setPotencia(int potencia) {
this.potencia = potencia;
}
public Ordenador(String id, String descripcion, int potencia) {
super();
this.id = id;
this.descripcion = descripcion;
this.potencia = potencia;
}

}


package com.arquitecturajava;

public class Lavadora {
private String id;
private String modelo;
private String descripcion;


public Lavadora(String id, String modelo, String descripcion) {
super();
this.id = id;
this.modelo = modelo;
this.descripcion = descripcion;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getModelo() {
return modelo;
}
public void setModelo(String modelo) {
this.modelo = modelo;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
}

Ambas clases disponen de las propiedades descripción e id. Sin embargo las clases no están relacionadas de ninguna forma. No son hijas de la clase Producto ni implementan un interface Producto.

Java Reflection clases Java

Así que trabajamos con ellas de forma independiente.

package com.arquitecturajava;

import java.util.ArrayList;
import java.util.List;

public class Principal {

	public static void main(String[] args) {
		List<Ordenador> lista = new ArrayList<Ordenador>();

		Ordenador o1 = new Ordenador("A1", "Ordenador gaming", 4);
		Ordenador o2 = new Ordenador("B1", "Ordenador ofimatica", 2);
		lista.add(o1);
		lista.add(o2);

		for (Ordenador o : lista) {

			System.out.println(o.getId());
			System.out.println(o.getPotencia());
			System.out.println(o.getDescripcion());

		}
		
		Lavadora l1= new Lavadora("L1","Standard","Lavadora normal");
		Lavadora l2= new Lavadora("L2","VIP","Lavadora avanzada");
				
		List<Lavadora> lista2= new ArrayList<Lavadora>();
		lista2.add(l1);
		lista2.add(l2);

		for (Lavadora l : lista2) {

			System.out.println(l.getId());
			System.out.println(l.getModelo());
			System.out.println(l.getDescripcion());

		}

	}

}

Creamos dos listas e imprimimos la información por la consola:

Java Reflection Consola

No nos queda más remedio que recorrerlas una a una lo cual es un problema. Las soluciones más habituales a estos casos es crear interfaces  o usar clases abstractas. De tal forma que a través del polimorfismo podamos trabajar de la misma forma con tipos muy dispares.

Java Reflection Herencia

El problema comienza cuando por ejemplo tenemos cientos de tipos diferentes y NO disponemos del código fuente.

Java Reflection JAR

Esto es un verdadero problema ya que nos posible modificar las clases existentes. ¿Cómo podemos abordar este problema y tratar de las misma forma las Lavadores y los Ordenadores y otros tipos?

Java Reflection

Es aquí donde el API de reflection nos puede rescatar leyendo los metadatos de nuestros objetos e invocando los métodos necesarios .


package com.arquitecturajava;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class Principal2 {

	public static void main(String[] args) {
		List<Ordenador> lista = new ArrayList<Ordenador>();

		Ordenador o1 = new Ordenador("A1", "Ordenador gaming", 4);
		Ordenador o2 = new Ordenador("B1", "Ordenador ofimatica", 2);
		lista.add(o1);
		lista.add(o2);

	
		Lavadora l1= new Lavadora("L1","Standard","Lavadora normal");
		Lavadora l2= new Lavadora("L2","VIP","Lavadora avanzada");
				
		List<Lavadora> lista2= new ArrayList<Lavadora>();
		lista2.add(l1);
		lista2.add(l2);
		
		imprimirListaCualquiera(lista);
		imprimirListaCualquiera(lista2);
		
	
	}
	
	public static void imprimirListaCualquiera(List<?> lista) {
		
		for(Object c: lista) {
			
			Method[] metodos=c.getClass().getMethods();
			for(Method m: metodos) {
				
				//System.out.println(m.getName());
				
				if (m.getName().equals("getId") || m.getName().equals("getDescripcion")) {
					
					try {
						String cadena=(String) m.invoke(c, null);
						System.out.println(cadena);
					} catch (IllegalAccessException | IllegalArgumentException
							| InvocationTargetException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}
		
	}

}

En este caso generamos un método “imprimirListaCualquiera”  que accede al API de reflection y lee los metadatos de cada objeto.

  1. En primer lugar utiliza un bucle para recorrer el Array y lee la lista de métodos que cada objeto contiene con  reflection y getMethods();
  2. En segundo lugar verifica que el objeto dispone de los métodos getId()y getDescripcion() usando m.getName().
  3. Por último usa el método invoke del API de reflection para invocar el método que corresponda.

El resultado por la consola será similar, eso sí en nuestro caso solo hemos solicidado getId() y getDescripcion():

La ventaja es que con construir un único método ha sido suficiente .

Java Reflection

 

Para poder usar Java Reflection tendremos que sacrificar algo y ese algo es el tema de rendimiento. El código se ejecutará bastante más despacio que un simple código con bucles . Pero en mas de una ocasión nos puede salvar de muchas horas de desarrollo.

Otros artículos relacionados:

  1. Java Override y encapsulación
  2. Herencia y relaciones entre objetos
  3. Java Generics (II) uso de WildCard
It's only fair to share...Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn

About Cecilio Álvarez Caules

Cecilio Álvarez Caules Sun Certified Enterprise Architech (J2EE/JEE).

8 Responses to El concepto Java Reflection y como utilizarlo

  1. jose 25 Marzo, 2017 at 16:34 #

    Hola Cecilio, lo primero felicitarte por tu web.
    No termino de ver claro los problemas que ves en las interfaces: “El problema comienza cuando por ejemplo tenemos cientos de tipos diferentes y NO disponemos del código fuente.” Para eso están las APIs y la documentación, para que aunque no tengamo código fuente podamos utilizar una librería y adaptarla a nuestra necesidades. ¿Cientos de tipo diferentes dentro de una misma jerarquía? ahora mismo no se me ocurre ningún ejemplo.
    Respecto al otro punto “Esto es un verdadero problema ya que nos posible modificar las clases existentes. ¿Cómo podemos abordar este problema y tratar de las misma forma las Lavadores y los Ordenadores y otros tipos?” Con la API se podría modificar el comportamiento utilizando herencia y sobreescribiendo el método.

    Para mí es mLucho más mantenible y optimo utilizar interfaces. Una interfaz con un método getDescripcion que implementas en las clases que necesites. Luego haces un método al que le pasas esa interfaz y es genérica para todos los que hereden de ella. Por si no queda muy claro con mis palabras, pongo ejempo:

    public interface Electrodomestico {
    public void getInfo();
    }

    public class Lavadora implements Electrodomestico{
    @Override
    public void getInfo() {
    System.out.println(“Esto es una lavadora”);
    }
    }

    public class Ordenador implements Electrodomestico{
    @Override
    public void getInfo() {
    System.out.println(“esto es un ordenador”);
    }
    }

    public class AppMain {
    public static void main(String[] args) {
    getInfo(new Lavadora());
    getInfo(new Ordenador());
    }
    public static void getInfo(Electrodomestico electrodomestico){
    electrodomestico.getInfo();
    }
    }

    • Cecilio Álvarez Caules 25 Marzo, 2017 at 19:39 #

      Estoy perfectamente de acuerdo contigo , es mejor usar un interface si es posible. Pero imaginate que te pasan un jar que tiene 50 clases similares pero no tienes implementados interfaces. Alguien no hizo bien su trabajo entonces ¿qué haces?. Te creas 50 adaptadores con sus particularidades para obligar a implementar el interface. O quizás simplemente te apoyas en el API de reflection y cubres el problema.

      Esa es un poco la decisión. En este caso se ha usado reflection pero un diseño correcto sería algo como lo tuyo en tal caso no haría falta usar reflection 🙂

      • jose 28 Marzo, 2017 at 0:02 #

        Gracias por tu respuesta

  2. Carlo 21 Marzo, 2017 at 22:49 #

    Gracias por la explicación Cecilio…. aun que no puedo entender porque se ejecuta mas lento que una invocacion “normal” a un metodo de una instancia de una clase.

    Saludos

    • Jonatan Quintero 22 Marzo, 2017 at 17:21 #

      Es mas lento debido a toda la lógica que hay que agregar para traer los metadatos, verificar que la clase posee el método, etc

      • Cecilio Álvarez Caules 22 Marzo, 2017 at 20:35 #

        Si claramente

        • Gabriel Hernandez 23 Marzo, 2017 at 20:06 #

          Hola Cecilio. Disculpa la molestia.
          Soy de la vieja escuela de la informatica es decir trabaje en Assembler, PL1, Cobol y Natural y aunque he hecho cursos de Java SE y ya he desarrollado algo en ello, hay conceptos que no he podido aclarar.
          Por ejemplo si he desarrollado algo en SE, podre utilizarlo via WEB posteriormente, es decir que un usuario pueda ingresar a mi aplicacion via WEB con los cambios necesarios? o siempre tendre que ejecutarla instalada en el servidor del cliente.

          • Cecilio Álvarez Caules 27 Marzo, 2017 at 14:18 #

            La pregunta es un poco peculiar , hoy por hoy si has programado con Java SE y Swing deberías desplegar la app como aplicación de escritorio. Si la desarrollas como aplicacion web necesitaras un servidor si o sí

Deja un comentario