Java: loading dinamico e reflection

Java: loading dinamico e reflection
Scopo del corso integrativo:
–
Presentare le caratteristiche avanzate di Java
che lo rendono un linguaggio molto flessibile
pur senza rinunciare ai vantaggi di un typechecking forte ed ad una fase compilativa
importante.
Premessa
Java è un linguaggio ”Data-Driven” come
tutti i linguaggi più noti (C, C++, Cobol,
....). Questo significa che non è possibile
scrivere un programma che modifica se
stesso, attività tipica di una applicazione
di AI (ad es il LISP)
Gradi di Dinamismo
Nonostante la premessa Java permette di
fare 2 cose importanti:
–
Caricamento dinamico di classi che possono
modificare il comportamento applicativo
(loading dinamico)
–
Analisi di classi ed oggetti per scoprire la
loro struttura pubblica, quali metodi
espongono, .... (reflection)
Loading Dinamico
Permette di realizzare una
implementazione pulita del concetto di
plugin. Sostanzialmente è possibile
scrivere un programma che decide quali
classi caricare ed utilizzare rendendo, di
conseguenza, il comportamento a runtime completamente dinamico.
Capire con un esempio
Per capire il concetto precedente
facciamo un esempio. Immaginiamo una
applicazione che è in grado di eseguire
una delle 4 operazioni elementari fra interi
(+, *, -, /). Quale delle quattro operazioni
eseguire può essere configurata mediante
un file.
Il Main
//Codice non compilante per mancanza
gestione eccezioni
public class BigBang{
static void main (String[] args) {
String oper = ”Somma”;
Class c = Class.forName(oper);
Operazione op=(Operazione)c.newInstance();
op.qualeOpEsegui();
op.execute(3,5);
}
L'interfaccia comune
public interface Operazione {
public void qualeOpEsegui();
public void execute (int a, int b);
}
La classe Somma
public class Somma implements Operazione {
public void qualeOpEsegui(){
System.out.println(”io faccio la somma”);
}
public void execute (int a, int b) {
System.out.println (a+”+”+b+”=”+(a+b) );
}
}
La classe Moltiplicazione
public class Moltiplicazione
implements Operazione {
public void qualeOpEsegui(){
System.out.println(”io faccio la
moltiplicazione”);
}
public void execute (int a, int b) {
System.out.println
(a+”*”+b+”=”+(a*b) );
}
Esecuzione
Se eseguimo il precendente main
otteniamo in output:
io faccio la
somma
3+5=8
Esecuzione 2
Se cambiamo il rigo:
String operazione = ”somma”;
con
String operazione = ”moltiplicazione ”;
Otterremmo in output:
io faccio la
moltiplicazione
3*5=15
Cosa succede
Il metodo Class.forName permette di
caricare dinamicamente una classe
spedificandone il nome mediante una
stringa. La stringa può essere composta
come volete ed ovviamente potrebbe
essere presa da un file di configurazione
dell'applicazione.
Cosa succede
Il metodo Class.forName ritorna un
oggetto di tipo Class ”cioè una classe”.
Questo significa che gli oggetti di tipo
Class sono delle ”metaclassi” che
possono essere manipolate a run-time.
L'oggetto c ottenuto può essere istanziato
(newInstance) per ottenere un oggetto
della classe che è stata caricata, cioè un
oggetto della classe Somma.
Cosa succede
se cambio LA SOLA STRINGA OPER in
moltiplicazione l'applicazione modifica il
suo comportamento a run-time in quanto
caricherà la classe Moltiplicazione e non
la classe Somma ed eseguirà l'operazione
corrispondente.
Introspezione o Reflection
Java permette di analizzare la struttura di
una classe dinamicamente.
In modo analogo è possibile chiedere
l'esecuzione dinamica di un metodo.
Reflection
Tutte le classi (oltre alla già nota Class)
sono contenute nel package
java.lang.reflect. Il package dal nome
simile (java.lang.ref) non ha nulla a che
fare con la reflection (ref = references).
Prendere la Classe dell'oggetto
E' necessario avere una istanza
dell'oggetto Class ( c ). E' ottenibile
mediante un Class.forName oppure
mediante l'applicazione del metodo
getClass() ad un oggetto.
Metodi Principali
I metodi principali di Class sono:
–
getName(): restituisce il nome della classe fully
qualified
–
getMethods: restituisce un array di Method che
rappresentano tutti i metodi accedibili di quella
classe
–
getMethod(String name, Class[] parameters):
restituisce il metodo di nome name e con i tipi di
parametri dati in Class
–
getFields(): che restituisce tutti gli attributi accedibili
della classe in un Field[]
Method
Mediante l'uso della classe Method posso chiedere
l'esecuzione di metodi. Es:
–
Class c = Class.forName(“Somma”);
–
Somma s = (Operazione) c.newInstance();
–
Method m =
c.getMethod(“qualeOpEsegui”,null);
–
m.invoke(s,null);
Field
Mediante l'uso della classe Field si
possono leggere e scrivere gli attributi di
una classe (quelli accedibili).
–
Aereo a = new Aereo();
–
Class c = a.getClass();
–
Field f = a.getField(“velocita”);
//darebbe errore. Perchè?
–
float v = f.getFloat(a);
–
v=v+10;
–
f.setFloat(a,v);
Tutti i membri di classe
Con i predenti metodi possiamo accedere
e modificare i membri accedibili di una
classe. E' possibile accedere anche a
quelli private con la limitazione di non
poter leggere o scrivere valori ma solo
guardare la struttura. Questi si ottengono
mediante l'uso dei metodi:
–
c.getDeclaredMethods,
–
c.getDeclaredFields,
–
...