La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

Técnicas avanzadas de programación Introspección

Presentaciones similares


Presentación del tema: "Técnicas avanzadas de programación Introspección"— Transcripción de la presentación:

1 Técnicas avanzadas de programación Introspección

2 Introducción Java tiene interesantes mecanismos para cargar clases de manera dinámica, conocer sus métodos y atributos, etc. Son formas de obtener información interna de las clases, incluso los objetos Java pueden informarnos de su propia estructura. Por ello se llaman utilidades de introspección. La clase más importante es java.lang.Class. Es una clase que nos permite describir cualquier clase Java. Dicho de otra forma, es un descriptor o referencia de una clase. Un paquete relevante es java.lang.reflect

3 Conocer el tipo En tiempo de ejecución puede saber el tipo (clase) que manejamos: Si tenemos un objeto, puedo conocer su clase: Coche miCoche = new Coche(); Class clase = miCoche.getClass(); System.out.println("Clase:" +clase.getName() ); Lo que hemos hecho es obtener un descriptor de clase (tipo Class) mediante miCoche.getClass(). Este descriptor nos devuelve su nombre mediante getName() Si tenemos una clase (no hay instancias): Coche.class.getName() getName() nos devuelve el nombre de la clase, incluyendo la jerarquía de paquetes. En nuestro ejemplo: newInstance.dominio.Coche

4 Carga dinámica Podemos cargar de manera dinámica un objeto, es decir, determinar en tiempo de ejecución (y no en tiempo de programación) la clase que vamos a instanciar Primero haremos una pequeña clase (Coche) de prueba: package newInstance.dominio; public class Coche extends Vehiculo { private int plazas = 5; public Coche() {} public Coche( int plazas ) { this.plazas = plazas; } public int getPlazas() { return plazas; } public void setPlazas( int p ) { plazas = p; } public String toString() { try { return super.toString() + " Plazas:" + String.valueOf(plazas); } catch (Exception e) { return "-1"; } } A continuación crearemos un objeto del tipo Class, que es un descriptor de la estructura de datos o clase. Lo conseguimos con forName(). El paso siguiente es crear una instancia de la clase con newInstance(). Class clase = Class.forName( “newInstance.dominio.Coche” ); Object objeto = clase.newInstance(); System.out.println(“Coche:" + objeto.toString() ); Necesitamos al menos un constructor sin parámetros (Coche()) para el uso de newInstance() o bien no especificar ninguno. Pero, si se especifica uno que tenga parámetros (Coche( int plazas )), entonces debe también implementar uno sin parámetros, ya que este es el que usa newInstance() Hemos determinado la clase desde una cadena en el código (newInstance.dominio.Coche). Esto no es muy dinámico. Pero las posibilidades de carga dinámica son evidentes: Un ejemplo puede ser que en función de una acción del usuario podemos instanciar una clase u otra. En un archivo properties podemos tener la asociación de acciones y clases. Otro ejemplo: puedo cargar todas las clases que haya en un determinado directorio, sin tener que determinar estáticamente (a priori en el código) las clase que cargo.

5 Carga dinámica. Ejemplo (I)
Vamos a instanciar todas las clases de un directorio (paquete). strDirClases se obtiene previamente desde un archivo properties: …. //// Obtengo directorio de clases File fDirClases = new File( strDirClases ); if ( !fDirClases.isDirectory()) { System.out.println("El directorio de clases no existe" ); return; } //// Obtengo clases del directorio File[] listaFicheros = fDirClases.listFiles( new ClassFileFilter() ); // Ver siguiente transparencia if (listaFicheros.length == 0) { System.out.println("No hay clases en el directorio de clases" ); //// Instancio clases System.out.println("\nClases instanciadas:" ); String strClase = ""; Object objeto = null; for (int i = 0; i < listaFicheros.length; i++) { // Quito el sufijo .class strClase = listaFicheros[i].getName().substring(0, listaFicheros[i].getName().indexOf(".")); //// lec es un lector de parámetros de archivo properties (en este ejemplo el paquete es “newInstance.dominio.”) Class clase = Class.forName( lec.getParametro("paquete") + strClase ); objeto = clase.newInstance(); System.out.println("Clase:" + strClase + " Objeto:" + objeto.toString() ); Resultado: Clases instanciadas: Clase:Coche Objeto:Id:1 Plazas:5 Clase:Vehiculo Objeto:Id:2

6 Carga dinámica. Ejemplo (II)
En este ejmplo se obtienen los archivos .class del directorio fDirClases (tipo File) por medio de la llamada: File[] listaFicheros = fDirClases.listFiles( new ClassFileFilter() ); listFiles usa un filtro de ficheros para obtener sólo los .class. El filtro es una clase que implementa el interfaz java.io.FileFilter, lo que le obliga a implementar el método accept(). Este método devuelve true si el archivo (parámetro) pasa el filtro: public class ClassFileFilter implements java.io.FileFilter { public boolean accept(java.io.File f) { if (!f.isDirectory()) { String name = f.getName().toLowerCase(); return name.endsWith("class"); } return false;

7 Manejo de la estructura de la clase (I)
Podemos obtener la clase madre, los atributos, constructores y métodos de una clase. Para ello usaremos java.lang.reflect. Para hallar la clase madre: Class clase = Class.forName( "newInstance.dominio.Coche"); Class claseMadre = clase.getSuperclass(); System.out.println( "Clase " + clase.getName() + ", hereda de " +claseMadre.getName()); Para atributos, métodos y constructores es muy sencillo: los atributos se representan por la clase Field, los constructores por Constructor y Method para los métodos. Partimos del descriptor de clase: El descriptor de clase tiene los métodos getFields(), getConstructors() y getMethod(), que nos devuelven arrays de los objetos que buscamos. Pero sólo nos devuelve elementos públicos, hay una versión “Declared” de cada método get que nos devuelve cualquier elemento (privado, protegido o público), por ejemplo: getDeclaredFields(). Field atrib[] = clase.getDeclaredFields(); // getFields() nos da solo los public printLista( "Atributos:", atrib ); //// Constructores y métodos Constructor cons[] = clase.getConstructors(); // Sólo public printLista( "Constructores:", cons ); …. public static void printLista( String titulo, Object obj[]) { System.out.println( titulo ); for ( int i = 0; i < obj.length; i++) System.out.println( " " + obj[i].toString()); }

8 Manejo de la estructura de la clase (II)
Además podemos invocar un método. Primero debemos encontrarlo, para lo cual usamos el método getMethod(), especificando el nombre del método y los tipos de sus parámetros. En este caso buscamos el método “setPlazas”, que tiene un parámetro int: Class tiposParam[] = { int.class }; Method metSetPlazas = clase.getMethod( "setPlazas", tiposParam ); A continuación lo invocamos. También necesitamos un array, pero esta vez para los valores de los parámetros: Coche miCoche = new Coche(); Object argumentos[] = {7}; metSetPlazas.invoke( miCoche, argumentos ); Invoke: si el método es static el primer argumento (miCoche en nuestro ejemplo) puede ser null. El segundo puede ser null si no tiene parámetros, en concreto (Object[]) null. Para comprobar el resultado puede usar miCoche.toString()

9 Midiendo el tiempo Hay una forma sencilla de medir el tiempo de invocación de un método, con System.currentTimeMillis(): //// Busca método toString (de otra forma, recorriendo array de Method) y lo invoca //// Ojo: búsqueda imperfecta, en caso de sobrecarga de métodos, toma el primero //// Además con System.currentTimeMillis() medimos tiempo de llamada for ( int i = 0; i < mets.length; i++) { if ( mets[i].getName().equals( "toString") ) { System.out.println( "Encontrado método " + mets[i].getName()); long inicio = System.currentTimeMillis(); String cadena = (String) mets[i].invoke( miCoche, (Object[]) null); long fin = System.currentTimeMillis(); System.out.println( "Invocado método: " + cadena); System.out.println( "Tiempo empleado por invoke(): " + (fin - inicio)); }


Descargar ppt "Técnicas avanzadas de programación Introspección"

Presentaciones similares


Anuncios Google