sabato 31 gennaio 2009
STOP al PRECARIATO
Comunicazione di servizio: mercoledì mi è arrivata la lieta e attesissima notizia che la mia azienda da mercoledì prossimo convertirà il mio contratto d'inserimento in contratto a tempo indeterminato. Giubilate gente! Di conseguenza mi prendo il we per festeggiare, ma tornerò presto :) Stay tuned
sabato 24 gennaio 2009
Initializations block
Avevo la seguente necessità: nel mio codice si fa spesso riferimento ad una costante; nel mio caso però il valore di questa costante non è cablato nel codice (o "inchiodato" come dice il mio capo al lavoro), ma è letto da un file di properties attraverso il metodo statico getString(String key) della classe Config.
Purproppo il mio metodo getString lancia un'eccezione nel caso fallisca la lettura dal file, quindi il compilatore rifiuta una scrittura del tipo:
public static final int NUM = Integer.parseInt(Config.getString("costante.numerica"));
*si noti che se il metodo getString non lanciasse eccezioni checked tale istruzione sarebbe accettata.
Mi son da subito venute in mente due soluzioni, poco eleganti ma che avrebbero funzionato: la prima era quella di scrivermi un wrapper per il getString che non lanciasse eccezioni, la seconda quello di rendere la costante private ed utilizzare un metodo statico per leggerla che la iniziasse nel caso non avesse ancora valore (in questo caso non poteva più essere dichiarata final e utilizzavo un wrapper in modo da verificare il non assegnamento col valore null, in pratica avrei simulato una costante con un campo non mutable); non vi dico che nessuna delle due soluzioni mi facesse saltare di gioia ;-)
Quando ormai ero quasi rassegnato ho trovato una soluzione di quelle che piaciono a me: static initializations block. Sinceramente prima di imbattermici contro (studiando per ottenere la certificazione Sun CX-310-065 ) non sapevo neanche si potesse fare qualcosa del genere in Java ;-). Sono blocchi di codice che vengono eseguiti una singola volta quando la classe viene caricata per la prima volta.
Il codice prodotto suona così:
public class Constants {
public static final int NUM;
static {
long j = 0;
try {
j = Integer.parseInt(Config.getString("costante.numerica"));
}
catch (Exception e) {
//non dovrei mai arrivare in questa situazione
assert(false);
}
NUM = j;
}
}
Una nota per concludere: possono essere presenti più di un blocco static: essi verranno eseguiti tutti, nell'ordine in cui compaiono nel codice.
Accanto ai blocchi statici ci sono gli instance initialization blocks. Come dice il nome questi ultimi sono legati alle istanze e vengono eseguiti ogni volta che viene creata un'istanza di una classe... ma questa è un'altra storia!
Purproppo il mio metodo getString lancia un'eccezione nel caso fallisca la lettura dal file, quindi il compilatore rifiuta una scrittura del tipo:
public static final int NUM = Integer.parseInt(Config.getString("costante.numerica"));
*si noti che se il metodo getString non lanciasse eccezioni checked tale istruzione sarebbe accettata.
Mi son da subito venute in mente due soluzioni, poco eleganti ma che avrebbero funzionato: la prima era quella di scrivermi un wrapper per il getString che non lanciasse eccezioni, la seconda quello di rendere la costante private ed utilizzare un metodo statico per leggerla che la iniziasse nel caso non avesse ancora valore (in questo caso non poteva più essere dichiarata final e utilizzavo un wrapper in modo da verificare il non assegnamento col valore null, in pratica avrei simulato una costante con un campo non mutable); non vi dico che nessuna delle due soluzioni mi facesse saltare di gioia ;-)
Quando ormai ero quasi rassegnato ho trovato una soluzione di quelle che piaciono a me: static initializations block. Sinceramente prima di imbattermici contro (studiando per ottenere la certificazione Sun CX-310-065 ) non sapevo neanche si potesse fare qualcosa del genere in Java ;-). Sono blocchi di codice che vengono eseguiti una singola volta quando la classe viene caricata per la prima volta.
Il codice prodotto suona così:
public class Constants {
public static final int NUM;
static {
long j = 0;
try {
j = Integer.parseInt(Config.getString("costante.numerica"));
}
catch (Exception e) {
//non dovrei mai arrivare in questa situazione
assert(false);
}
NUM = j;
}
}
Una nota per concludere: possono essere presenti più di un blocco static: essi verranno eseguiti tutti, nell'ordine in cui compaiono nel codice.
Accanto ai blocchi statici ci sono gli instance initialization blocks. Come dice il nome questi ultimi sono legati alle istanze e vengono eseguiti ogni volta che viene creata un'istanza di una classe... ma questa è un'altra storia!
domenica 18 gennaio 2009
Java Reflection per settare le proprietà di un bean
Ultimamente mi sono scontrato con il seguente problema: leggere dei dati da un file xml e caricarli in un bean. L'xml forniva un elenco di proprietà passate gestite come coppie chiave (id) - valore (value).
L'idea che mi è venuta è stata quella di approfittare di questo task banale per sperimentare un aspetto della programmazione java che molto mi affascina, ma che troppo raramente ho occasione di utilizzare: la reflection.
Ho creato un bean avente ogni proprietà (con relativo setter) che si chiama proprio come un elemento "property" del file xml.
A questo punto ho sfruttato la potenza della reflection per inserire la lettura delle proprietà all'interno di un ciclo for, col vantaggio di scrivere il codice una volta sola per tutte le proprietà.
(avrei potuto arrivare allo stesso risultato utilizzando una HashMap come struttura dati, ma a quel punto dove sarebbe stato il divertimento? edit: e comunque le due soluzioni hanno pro e contro)
Quindi per prima cosa creaimo il bean:
public class MyBean {
private String nome;
private String cognome;
//aggiungere tutte le proprietà del file xml, con relativi setters e getters
public String getNome(){return nome;}
public String getCognome(){return cognome;}
public String setNome(String nome){this.nome = nome;}
public String setCognome(String cognome){this.cognome = cognome;}
}
Il cuore del ciclo for è riportato di seguito:
MyBean bean = new MyBean();
//Per ogni proprietà
for (int j=0; j < propertyElements.size(); ++j) {
Element property = (Element)propertyElements.item(j);
name = property.getAttribute("id");
value = ((Element)property.getElementsByTagName("value").item(0)).getAttribute("id");
try {
Method setter = bean.getClass().getMethod(getMethodName(name), new Class[] { value.getClass( ) });
setter.invoke(bean, new Object [] { value } );
}
catch (Exception e) {
//Mi limito a loggare l'eccezione ignorando la proprietà non prevista
log.error("Error (" + name + ")",e);
}
}
dove il metodo getMethodName è così definito
//name -> getName
private String getMethodName(String name) {
return new StringBuilder("get")
.append(name.substring(0,1).toUpperCase())
.append(name.subString(1)).toString;
}
Analizziamo il codice nel blocco try-catch:
nella prima riga viene costruito l'oggetto Method, che rappresenta il metodo che verrà invocato. Per fare questo si invoca il metodo getMethod() sulla classe Bean. Tale metodo riceve due argomenti: una stringa contenente il nome del metodo, ed un array di Class, che rappresentano i tipi dei parametri formali (non i valori attuali) che il metodo si aspetta. Questo secondo parametro è importantissimo perchè permette di gestire l'overloading dei metodi.
E' nella seconda linea che il metodo viene invocato, tramite la chiamata al metodo invoke. Questa riceve due parametri: il primo è il riferimento all'oggetto su cui il metodo deve essere invocato, il secondo è un array contenente i parametri attuali. Il metodo ritorna un object contenente il risultato dell'invocazione (che io ho ignorato essendo il mio getXXX dichiarato void).
Questo codice è stato inserito in un blocco try-catch perchè l'uso della reflection può sollevare diverse eccezioni, che dettagliano il problema nel caso l'invocazione fallisse:
SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
Per avere maggiori informazioni su queste eccezioni si rimanda alla documentazione del JDK.
Chiudo sottolineando quelli che sono i due grossi svantaggi dell'utilizzo della reflection:
L'idea che mi è venuta è stata quella di approfittare di questo task banale per sperimentare un aspetto della programmazione java che molto mi affascina, ma che troppo raramente ho occasione di utilizzare: la reflection.
Ho creato un bean avente ogni proprietà (con relativo setter) che si chiama proprio come un elemento "property" del file xml.
A questo punto ho sfruttato la potenza della reflection per inserire la lettura delle proprietà all'interno di un ciclo for, col vantaggio di scrivere il codice una volta sola per tutte le proprietà.
(avrei potuto arrivare allo stesso risultato utilizzando una HashMap come struttura dati, ma a quel punto dove sarebbe stato il divertimento? edit: e comunque le due soluzioni hanno pro e contro)
Quindi per prima cosa creaimo il bean:
public class MyBean {
private String nome;
private String cognome;
//aggiungere tutte le proprietà del file xml, con relativi setters e getters
public String getNome(){return nome;}
public String getCognome(){return cognome;}
public String setNome(String nome){this.nome = nome;}
public String setCognome(String cognome){this.cognome = cognome;}
}
Il cuore del ciclo for è riportato di seguito:
MyBean bean = new MyBean();
//Per ogni proprietà
for (int j=0; j < propertyElements.size(); ++j) {
Element property = (Element)propertyElements.item(j);
name = property.getAttribute("id");
value = ((Element)property.getElementsByTagName("value").item(0)).getAttribute("id");
try {
Method setter = bean.getClass().getMethod(getMethodName(name), new Class[] { value.getClass( ) });
setter.invoke(bean, new Object [] { value } );
}
catch (Exception e) {
//Mi limito a loggare l'eccezione ignorando la proprietà non prevista
log.error("Error (" + name + ")",e);
}
}
dove il metodo getMethodName è così definito
//name -> getName
private String getMethodName(String name) {
return new StringBuilder("get")
.append(name.substring(0,1).toUpperCase())
.append(name.subString(1)).toString;
}
Analizziamo il codice nel blocco try-catch:
nella prima riga viene costruito l'oggetto Method, che rappresenta il metodo che verrà invocato. Per fare questo si invoca il metodo getMethod() sulla classe Bean. Tale metodo riceve due argomenti: una stringa contenente il nome del metodo, ed un array di Class, che rappresentano i tipi dei parametri formali (non i valori attuali) che il metodo si aspetta. Questo secondo parametro è importantissimo perchè permette di gestire l'overloading dei metodi.
E' nella seconda linea che il metodo viene invocato, tramite la chiamata al metodo invoke. Questa riceve due parametri: il primo è il riferimento all'oggetto su cui il metodo deve essere invocato, il secondo è un array contenente i parametri attuali. Il metodo ritorna un object contenente il risultato dell'invocazione (che io ho ignorato essendo il mio getXXX dichiarato void).
Questo codice è stato inserito in un blocco try-catch perchè l'uso della reflection può sollevare diverse eccezioni, che dettagliano il problema nel caso l'invocazione fallisse:
SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
Per avere maggiori informazioni su queste eccezioni si rimanda alla documentazione del JDK.
Chiudo sottolineando quelli che sono i due grossi svantaggi dell'utilizzo della reflection:
- il primo è legato alle prestazioni, in quanto la chiamata ad un metodo tramite reflection è meno performante dell'invocazione diretta del metodo;
- il secondo problema è legato alle potenzialità che la reflection offre: l'uso della reflection può nascondere al programmatore cosa realmente accade nel codice, rendendo più difficoltose le attività di manutenzione dello stesso
Iscriviti a:
Post (Atom)
