Tres Métodos para Obtener Objetos Class
Las operaciones de reflexión en Java requieren primero obtener un objeto Class. Existen tres métodos para obtener un objeto Class:
- Atributo público class
- Método getClass()
- Class.forName()
Ejemplo:
public class EjemploReflexionClase {
private static final Logger LOGGER = Logger.getLogger(EjemploReflexionClase.class);
public static void main(String[] args) throws ClassNotFoundException {
LOGGER.info("Obtener objeto Class método 01: atributo público class");
Class clase1 = Usuario.class;
LOGGER.info(clase1);
System.out.println();
LOGGER.info("Obtener objeto Class método 02: método getClass()");
Usuario usuario = new Usuario();
Class clase2 = usuario.getClass();
LOGGER.info(clase2);
System.out.println();
LOGGER.info("Obtener objeto Class método 03: Class.forName (requiere manejo de excepciones)");
Class clase3 = Class.forName("com.ejemplo.reflexion.modelo.Usuario");
LOGGER.info(clase3);
}
}
Dos Formas de Instanciar Objetos a Través de Reflexión
Además de usar el operador new para instanciar objetos, podemos hacerlo mediante reflexión de dos formas:
- Class.newInstance()
- Constructor.newInstance()
Ejemplo:
public class CreacionObjetosDemo {
private final static Logger LOGGER = Logger.getLogger(CreacionObjetosDemo.class);
public static void main(String[] args) throws Exception {
// Instanciación mediante new
Usuario usuario = new Usuario();
LOGGER.info("Instanciación mediante new: " + usuario.toString());
// Instanciación mediante reflexión
Class claseUsuario = Usuario.class;
// Método 01: Class.newInstance() (requiere casteo [constructor sin parámetros])
Usuario usuario1 = (Usuario) claseUsuario.newInstance();
LOGGER.info("Instanciación mediante reflexión 01-Class.newInstance(): " + usuario1.toString());
// Método 02: Constructor.newInstance() (requiere casteo [puede tener parámetros])
Constructor constructor = claseUsuario.getDeclaredConstructor();
Usuario usuario2 = (Usuario) constructor.newInstance();
LOGGER.info("Instanciación mediante reflexión 02-Constructor.newInstance() [sin parámetros]: " + usuario2.toString());
Constructor constructor1 = claseUsuario.getDeclaredConstructor(String.class, String.class);
Usuario usuario3 = (Usuario) constructor1.newInstance("María", "654321");
LOGGER.info("Instanciación mediante reflexión 02-Constructor.newInstance() [con parámetros]: " + usuario3.toString());
}
}
Obtener Todos los Constructores de una Clase
public class ReflexionConstructores {
public static void main(String[] args) {
Fecha fecha = new Fecha();
clase = fecha.getClass();
String nombreClase = clase.getName();
System.out.println(nombreClase);
Constructor[] constructoresDeclarados = clase.getDeclaredConstructors();
for (Constructor constructor : constructoresDeclarados) {
int modificadores = constructor.getModifiers();
System.out.print(Modifier.toString(modificadores) + " ");
System.out.print(constructor.getName() + "(");
Class[] tiposParametro = constructor.getParameterTypes();
for (Class tipoParametro : tiposParametro) {
System.out.print(tipoParametro.getName() + " ");
}
System.out.println(")");
}
}
}
Obtener Toda la Información de Atributos de una Clase
public class ReflexionAtributos {
public static void main(String[] args) {
Fecha fecha = new Fecha();
clase = fecha.getClass();
String nombreClase = clase.getName();
System.out.println(nombreClase);
Campo[] campos = clase.getDeclaredFields();
for (Campo campo : campos) {
String modificadores = Modifier.toString(campo.getModifiers());
Class tipo = campo.getType();
String nombre = campo.getName();
System.out.println(modificadores + " " + tipo.getName() + " " + nombre);
}
}
}
Obtener Toda la Información de Métodos de una Clase
public class ReflexionMetodos {
public static void main(String[] args) {
Fecha fecha = new Fecha();
clase = fecha.getClass();
Metodo[] metodos = clase.getDeclaredMethods();
for (Metodo metodo : metodos) {
String modificadores = Modifier.toString(metodo.getModifiers());
Class tipoRetorno = metodo.getReturnType();
String nombre = metodo.getName();
Class[] tiposParametro = metodo.getParameterTypes();
Class[] excepciones = metodo.getExceptionTypes();
System.out.println(modificadores + " " + tipoRetorno + " " + nombre
+ "(" + Arrays.asList(tiposParametro) + ")throws" + Arrays.asList(excepciones));
}
}
}
Proxy Dinámico
El proxy es uno de los patrones de diseño fundamentales. A continuación se muestra un ejemplo simple que ilustra la estructura del proxy:
interface Interfaz {
void realizarTarea();
void otraTarea(String arg);
}
class ObjetoReal implements Interfaz {
public void realizarTarea() { System.out.println("realizarTarea"); }
public void otraTarea(String arg) {
System.out.println("otraTarea " + arg);
}
}
class ProxySimple implements Interfaz {
private Interfaz objetoProxied;
public ProxySimple(Interfaz objetoProxied) {
this.objetoProxied = objetoProxied;
}
public void realizarTarea() {
System.out.println("ProxySimple realizarTarea");
objetoProxied.realizarTarea();
}
public void otraTarea(String arg) {
System.out.println("ProxySimple otraTarea " + arg);
objetoProxied.otraTarea(arg);
}
}
class DemoProxySimple {
public static void consumidor(Interfaz iface) {
iface.realizarTarea();
iface.otraTarea("ejemplo");
}
public static void main(String[] args) {
consumidor(new ObjetoReal());
consumidor(new ProxySimple(new ObjetoReal()));
}
}
El proxy dinámico de Java va un paso más allá en el concepto de proxy, ya que puede crear proxies dinámicamente y manejar dinámicamente las llamadas a los métodos proxyados. Todas las llamadas realizadas a través del proxy dinámico se redirigen a un único manejador de invocación.
import java.lang.reflect.*;
class ManejadorProxyDinamico implements InvocationHandler {
private objetoProxied;
public ManejadorProxyDinamico(Object objetoProxied) {
this.objetoProxied = objetoProxied;
}
public Object invoke(Object proxy, Metodo metodo, Object[] args) throws Throwable {
System.out.println("**** proxy: " + proxy.getClass() +
", metodo: " + metodo + ", args: " + args);
if(args != null)
for(Object arg : args)
System.out.println(" " + arg);
return metodo.invoke(objetoProxied, args);
}
}
class DemoProxyDinamicoSimple {
public static void consumidor(Interfaz iface) {
iface.realizarTarea();
iface.otraTarea("ejemplo");
}
public static void main(String[] args) {
ObjetoReal real = new ObjetoReal();
consumidor(real);
// Insertar un proxy y llamar nuevamente:
Interfaz proxy = (Interfaz)Proxy.newProxyInstance(
Interfaz.class.getClassLoader(),
new Class[]{ Interfaz.class },
new ManejadorProxyDinamico(real));
consumidor(proxy);
}
}
Objeto Nulo
Al usar el valor incorporado null para indicar la ausencia de un objeto, debes probar en cada uso si la referencia es nula, lo que resulta código tedioso y aburrido. El problema es que null, además de provocar una NullPointerException cuando intentas usarlo para realizar cualquier operación, no tiene otro comportamiento. A veces, resulta útil adoptar el concepto de objeto nulo, que puede aceptar los mensajes destinados al objeto que representa, pero devuelve valores que indican que no existe ningún objeto "real" de hecho. De esta manera, puedes suponer que todos los objetos son válidos sin tener que dedicar esfuerzo de programación a comprobar si son nulos.