¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
¡Acceso ilimitado 24/7 a todos nuestros libros y vídeos! Descubra la Biblioteca Online ENI. Pulse aquí
  1. Libros
  2. Aprender la Programación Orientada a Objetos con el lenguaje Java
  3. La reflexión
Extrait - Aprender la Programación Orientada a Objetos con el lenguaje Java (con ejercicios prácticos y corregidos)
Extractos del libro
Aprender la Programación Orientada a Objetos con el lenguaje Java (con ejercicios prácticos y corregidos) Volver a la página de compra del libro

La reflexión

Introducción

Ahora vamos a interesarnos por otra parte de la POO llamada -con más o menos éxito- reflexión. La «reflexión» en POO no es el hecho de reflexionar profundamente sobre algo, sino que se trata aquí de una radiografía de objetos desconocidos y compilados, cuyas propiedades y comportamientos vamos a descubrir sobre la marcha.

Por lo tanto, sería más adecuado asociar a esta «reflexión» el sentido de «reflejo», antes que el de meditación....

Pero ¿para qué hacerlo?

La pregunta es legítima: ¿por qué vamos a necesitar recuperar dinámicamente las características de los objetos?

Dos ejemplos:

  • El «plug-in»

    Ha diseñado una aplicación lúdica para que puedan formarse grandes y pequeños. La aplicación permite que el jugador se identifique, seleccione un tema para estudiar y divertirse (lo más importante para aprender). Entonces, la aplicación mide los tiempos de aprendizaje, ejercicios, cuenta las puntuaciones, las compara y determina incluso recompensas, etc. Un éxito.

    Los módulos pedagógicos todavía no están desarrollados porque necesita la participación de especialistas en geografía, historia, matemáticas, etc. Los jugadores podrán descargar estos nuevos módulos a medida que estén disponibles. Una vez recuperados, su aplicación los deberá reconocer e integrar, aunque sea para ignorarlos poco tiempo después. ¿Cómo conseguirlo?

    La «reflexión» será una solución elegante porque permitirá a su aplicación recorrer el directorio de descarga, «radiografiar» dinámicamente los paquetes que contiene para saber si soportan o no la interfaz Java que ha diseñado para integrarse con su juego (aquí hacemos referencia a las nociones...

Introspección de una clase Java

Gracias a la reflexión, vamos a poder recuperar dinámicamente desde un objeto:

  • el nombre de la clase original,

  • si esta clase es de acceso público, privado, etc.,

  • su clase madre,

  • qué interfaces soporta esta clase,

  • su constructor o sus constructores,

  • sus métodos,

  • sus propiedades,

  • información sobre su paquete de pertenencia y sus comentarios,

  • etc.

Se habla aquí de «metadatos», es decir, de información que describe un tipo de objeto.

Los entornos de desarrollo utilizan la reflexión para ayudarle en la escritura de sus programas.

Es la clase java.Lang.Class la que juega el rol de «recolector de información» entre un tipo de objeto y su programa. Todos los objetos Java -incluidos los más primitivos- pertenecen a una java.Lang.Class, y enseguida vamos a radiografiar la de String y pedirle la lista de todos los métodos.

package demo.reflexion;  
  
  
import java.lang.reflect.Method;  
  
public class Main {  
  
   public static void main(String[] args) {  
  
       Class stringClass = String.class;  
  
       Method[] methods = stringClass.getMethods();  
  
       for (Method method: methods) {  
     ...

Carga dinámica y utilización de una clase descubierta

La primera etapa de nuestro proyecto consiste en definir el ticket de entrada a nuestro supersistema de plug-in, es decir, un contrato que todo módulo deberá implementar para poder ser llamado.

 Para esto, vamos a crear un paquete que contenga únicamente una interfaz como la siguiente:

public interfaz IPlugin {  
  
    String AquiMiNombre();  
    Boolean JuegaConmigo();  
} 

Vamos a transformar el archivo .class generado como salida de este proyecto en un archivo .jar, que es el contenedor de entrega de los archivos Java compilados.

 Para esto, vamos a activar el menú File, opción Project Structure, seleccionar Artifacts y después, pulsando el botón +, agregar un JAR que contenga nuestro módulo y sus dependencias.

images/02ri10v2.png
images/03ri10v2.png
images/04ri10v2.png

La construcción del archivo .jar se realiza desde el menú Build, opción Build Artifacts.

images/05RI10V2.png

La primera etapa está terminada: tenemos un archivo InterfacePlugin.jar que contiene el contrato. Ahora vamos a situarnos en el lado del creador de plug-ins y vamos a crear una clase que va a implementar esta interfaz.

En este nuevo proyecto, para poder escribir una clase que implemente una interfaz, hay que agregar su librería.

 Para esto, vamos a activar el menú File, opción Project Structure, seleccionar Libraries y después, pulsando el botón +, agregar la librería Java.

images/06ri10v2.png
images/07RI10V2.png
images/08ri10v2.png

 A continuación, podemos escribir el código de nuestro plug-in:

import java.io.IOException;  
  
public class Pluginfo implements IPlugin {  
  
  
    public Pluginfo(){  
  
    }  
  
    @Override  
    public String AquiMiNombre() {  
        return "Cuestionario Informático";  
    }  
  
    @Override  
 ...

Ejercicio

 Cree un nuevo proyecto que contenga una clase que implemente la interfaz IPlugin. Vuelva a copiar su archivo .jar en el directorio de los plug-ins utilizado con anterioridad y compruebe que la aplicación CargadorPlugin lo reconoce y explota correctamente.

A continuación se muestra un ejemplo en consola de lo que esto podría dar:

images/17RI10V2.png

Privado, pero no tanto...

Gracias a los atributos private, hemos visto que haría falta «ocultar» determinados miembros de nuestras clases para protegerlos de usos malintencionados por parte de otros desarrolladores. Todo esto encaja perfectamente con la reflexión.

 Añada el siguiente método a uno de los plug-ins desarrollados con anterioridad:

private void AquiEsPrivate(){  
    System.out.println("Este acceso está prohibido ");  
} 

 Añada la siguiente secuencia al administrador de plug-ins:

Method privateMetod = o.getClass().getDeclaredMethod("AquiEsPrivate"); 
if(privateMetod!= null) {  
   privateMetod.setAccessible(true);  
   privateMetod.invoke(o);  
} 

 Vuelva a lanzar la aplicación y compruebe.

images/08RI06V2.png

Definitivamente, la reflexión es potente y permisiva, pero esto no ha terminado. Además de descubrir los miembros de las clases, también nos permite leer el código fuente.

Decompilación y ofuscación

Incluso si esto se sale un poco del marco de este libro, esta última sección trata sobre la protección de nuestros desarrollos contra la copia. Como se ha mencionado anteriormente, los .jar y otras .class se pueden convertir «fácilmente» en código fuente Java usando herramientas adecuadas. Estas herramientas se llaman «decompiladores». Se presentan como aplicaciones para instalar en su máquina e incluso como sitios Internet. Vamos a utilizar uno de ellos: http://www.javadecompilers.com

Recuerde el pequeño ejercicio del capítulo Herencia y polimorfismo, sobre las cuentas bancarias.

En primer lugar, vamos a tomar sus archivos .class -por lo tanto, archivos binarios generados por IntelliJ IDEA- para pasarlos al descifrador en línea.

 Conéctese al sitio web http://www.javadecompilers.com.

 En la pantalla de bienvenida, haga clic en el botón Seleccionar archivo o arrastre uno de los archivos .class a esta zona.

images/10RI10V2.png

 Haga clic en Upload and Decompile (transferir y desensamblar).

images/11ri10v2.png

 Al cabo de algunos instantes, el trabajo ha terminado y puede hacer clic en el botón Save.

images/12RI10V2.png

El código decompilado ya está en su PC en un archivo .zip. Dentro, el código fuente recuperado es este:

package labcuentabancaria;  
  
  
  
public class CuentaBancaria  
{  
 private static Integer numCont = Integer.valueOf(100);  
 private String Titular;  
 private Integer Numero;  
 private double Saldo;  
   
 public String getTitular() { return Titular; }  
   
 public final void setTitular(String Titular) {  
   this.Titular = Titular;  
 }  
   
  
 public Integer getNumero()  
 {  
   return Numero;  
 }  
   
 public final void setNumero(Integer Numero) { this.Numero = Numero; } 
   
  
  
 public double getSaldo()  
 {  
   return Saldo;  
 }  
   
 public void setSaldo(double Saldo) { this.Saldo = Saldo;...