I numerosi framework usati per realizzare applicazioni Java web based che, negli ultimi anni, hanno aiutato nella progettazione, creando dei modelli (Struts, Hibernate, gli EJB, ecc) tipici di sviluppo, sono legati da un aspetto comune che spesso non è noto ai più: la Reflection.
Questa peculiarità del linguaggio di programmazione Java va in qualche modo in controtendenza alla stessa idea di paradigma orientato agli oggetti, in quanto permette la manipolazione di classi, metodi e attributi in maniera non convenzionale al paradigma stesso. In questo articolo ci limiteremo ad illustrare il principio su cui si basa la reflection e a mostrare una serie di routine, utili per utilizzare gli strumenti base.
Le API reflection che trovate sotto il package java.lang.reflection
sono un insieme di classi pensate per l'interrogazione dinamica di istanze di classi java (proprietà riflessive). In questo modo, a partire da una generica classe possiamo interrogarla e conoscere i nomi dei suoi metodi, dei suoi attributi e di tutto ciò che essa contiene, direttamente durante la sua esecuzione. Addirittura è possibile interrogarne i metodi, o invocarli, come se ci si trovasse in un normale flusso di esecuzione.
La violazione dell'incapsulamento può essere un problema al paradigma object oriented ma, se usata con le giuste accortezze, la reflection diventa un potentissimo strumento utile allo sviluppo di framework e metodologie riutilizzabili.
Ha, per esempio, interessantissime applicazioni nella trasposizione di dati da un tipo ad un altro: molto spesso si usa la reflection per convertire documenti XML in oggetti Java e viceversa.
Vediamo di seguito una classe di utilità che presenta una serie di metodi statici, utili per interrogare alcune proprietà di un generico oggetto.
/* Listato 1 - ReflectionUtils.java
*
* Esporta una serie di metodi di utilità
* per utilizzare le proprietà riflessive
*/
public class ReflectionUtils {
public static String getClassName(Object aClass)
{
return aClass.getClass().getSimpleName();
}
// Tramite l'oggetto di cui non conosco la classe mi faccio
// restituire i suoi attributi in un array di stringhe
public static String[] getStringFields(Object aClass)
{
Field[] campi = aClass.getClass().getDeclaredFields();
String[] campiStringa = new String[campi.length];
for (int i = 0; i < campi.length; i++)
{
campiStringa[i] = campi[i].getName();
}
return campiStringa;
}
// ...
Nella seconda parte del codice dato il nome (name) di un attributo/campo, l'oggetto del bean (target) a cui settare il valore dell'attributo (value), individuo il metodo adatto (se c'è) e lo invoco.
/* Listato 2. (continua da 1)
*
* Individuare e invocare il metodo
*/
public static void setProperty(String name, Object target, Object value)
{
Method metodo = null;
String nameToUpperCase = checkCase(name);
try {
metodo = target.getClass().getMethod("set" + nameToUpperCase, new Class[] {value.getClass()});
}
catch (NoSuchMethodException e) { }
if (metodo != null)
try {
metodo.invoke(target, new Object[] {value});
}
catch (Exception ecc) { }
}
Infine nell'ultima parte, dato il nome (name) di un attributo/campo e l'oggetto del bean (target) individuo il metodo adatto (se c'è), lo invoco, recuperando il valore del risultato (come stringa).
/* Listato 3. (continua da 2)
*
* Individuare il metodo, invocarlo e recuperare
* il valore del risultato
*/
public static String getProperty(String name, Object target)
{
String ritorno = new String();
Method metodo = null;
String nameToUpperCase = checkCase(name);
try {
metodo = target.getClass().getMethod("get" + nameToUpperCase, null);
}
catch (NoSuchMethodException exc) { }
if (metodo != null)
try {
ritorno = (String) metodo.invoke(target, new Object[0]);
}
catch (Exception ecc) { }
return ritorno;
}
// ...
} // chiude la classe ReflectionUtils
Di notevole interesse sono i metodi getProperty()
e setProperty()
attraverso i quali riusciamo ad emulare il comportamento dei metodi accessori (get e set) della classe java (in un javabean, per convenzione dovranno essere presenti).