Un
nuovo SDK
Dopo quasi dieci anni di carriera, Java sta per entrare
in una nuova fase grazie ad alcune importanti aggiunte
ai costrutti del linguaggio. Dalla sua prima release,
Java ha subito solamente quattro modifiche a livello
di linguaggio, con un impatto molto lieve sulle abitudini
del comune programmatore:
JDK
1.0.2 - Rimozione della combinazione di modificatori
"private protected"
JDK 1.1 - Introduzione di Classi e Interfacce interne
J2SDK 1.2 - Aggiunta la keyword strictfp
J2SDK 1.4 - Aggiunta di un meccanismo di Assertion
Dopo
dieci anni di gestione conservativa, il J2SDK 1.5 [1]
si appresta ad introdurre ben sette nuovi costrutti,
la cui portata è tale da non lasciar spazio all'indifferenza.
Gli obiettivi dichiarati dai progettisti sono quattro:
-
Aumentare l'affidabilità delle dichiarazioni
di tipo
- Rendere
il codice più compatto
- Migliorare
la leggibilità
- Mantenere
la compatibilità all'indietro, sia a livello
di bytecode che di sorgente
Tre
di questi costrutti (Generics, foreach e Autoboxing/Unboxing)
sono indirizzati a semplificare la gestione di Array
e Collections. Un'altro costrutto, le enumeration, fornisce
un meccanismo standard per la definizione di insiemi
di oggetti. Infine, ma non meno importante, sono stati
introdotti metadati, import statico e metodi con numero
variabile di argomenti.
Curiosamente
le nuove features non hanno richiesto alcun cambiamento
alla Virtual Machine: esse vengono implementate nel
compilatore con un meccanismo tipo espansione di macro,
che produce bytecode compatibile con la precedente versione
della JVM. In secondo luogo, l'aggiunta di questi sette
nuovi costrutti ha richiesto una sola nuova keyword,
la parola "enum" che non può più
essere utilizzata come identificatore di metodo, classe
o variabile.
Inizia
l'era del C--?
Storicamente Java nasce come sottoinsieme del C++, un
linguaggio molto potente ma ricco di idiosincrasie e
di costrutti potenzialmente pericolosi. I progettisti
di Java, pur conservando la sintassi di base del C++,
decisero di rimuovere i costrutti più macchinosi,
come l'ereditarietà multipla e l'aritmetica dei
puntatori, dando vita ad un linguaggio compatto ed espressivo
che ha riscosso un enorme successo, sia in ambito accademico
che aziendale.
L'aggiunta
di queste sette nuove features costituisce una grossa
rottura con il passato: costrutti come i Generics o
le Enum presentano delle sottigliezze di utilizzo tanto
sul piano sintattico quanto su quello semantico, un
fatto che tradisce il design originale. Non deve pertanto
stupire che al partito degli entusiasti si contrapponga
un drappello di scettici, che inondano forum e newsgroup
di critiche, arrivando addirittura ad ipotizzare "La
fine di Java" [2].
La
somiglianza sintattica tra i nuovi costrutti Java e
quelli analoghi presenti nel C++ spaventa gli scettici,
che interpretano questo fenomeno come una pericolosa
frattura col design originario e temono ricadute disastrose
sul lungo termine.
Bisogna
tuttavia sottolineare che esistono sostanziali differenze
tra l'implementazione di queste features in Java e quella
delle omologhe presenti nel C++. Per fare un esempio,
i Generics Java, a differenza dei Templates C++, prevedono
un controllo statico sui tipi presenti nelle dichiarazioni
e, cosa ancor più importante, la loro implementazione
non prevede la generazione di codice extra ("Code
Bloat") per ogni realizzazione concreta del modello.
Per rendere l'idea di quale sia la differenza tra le
due implementazioni in termini di complessità,
è sufficiente pensare che se da una parte esistono
interi libri sulla Standard Template Library C++, dall'altra
è sufficiente un tutorial di venti pagine per
illustrare nei dettagli l'uso dei Generics in Java [6].
E'
chiaro pertanto che solo il tempo permetterà
di giudicare l'impatto reale di queste novità
sulla comunità degli utenti. Andiamo ora ad analizzare
queste novità una per una.
Generics
I Generics sono senza dubbio l'aspetto più interessante
e più atteso di questa nuova release di Java.
Per anni alcuni sviluppatori ne hanno lamentato l'assenza,
mentre altri ne criticavano la complessità. La
storia di Java e del polimorfismo parametrico è
piuttosto interessante, e merita di essere raccontata.
Un po' di storia
La comunità degli sviluppatori ha sempre lamentato
l'assenza di una forma di Polimorfismo Parametrico in
Java, qualcosa di simile ai Templates C++. James Gosling,
il capo del team di sviluppo di Java, scelse di non
includere questa feature proprio a causa della macchinosità
dei Templates C++.
Tuttavia,
già a partire dal 1996 era possibile reperire
su Intenet dei compilatori in grado di lavorare su versioni
potenziate di Java che includevano una qualche forma
di polimorfismo parametrico. La più famosa di
queste, conosciuta con l'italianissimo nome Pizza [3],
includeva anche altre caratteristiche tipiche dei linguaggi
funzionali, come funzioni di prima classe e tipi algebrici.
I progettisti di Pizza, convinti del potenziale della
loro implementazione, svilupparono GJ [4], una versione
semplificata di Pizza che aggingeva a Java il Polimorfismo
Parametrico e l'Overriding Covariante. Nel maggio del
'99 stilarono una documento da sottoporre al Java Community
Process [5] affinchè GJ diventasse parte integrante
del linguaggio Java. Dopo cinque anni di lavoro, passati
a vagliare i pro e i contro di questa architettura,
la Sun Microsystem ha riconosciuto la validità
della proposta, ed ha introdotto in Java il polimorfismo
parametrico nella forma proposta dal team di GJ.
Generics e Polimorfismo Parametrico
Il polimorfismo parametrico è un costrutto che
permette di delegare l'attribuzione del tipo di uno
o più attributi di una classe ad appositi parametri,
forniti alla classe al momento della creazione.
Grazie
a questo costrutto è possibile eliminare virtualmente
qualunque necessità di ricorrere al casting,
un'operazione fortemente sconsigliata ma necessaria
in tutte le circostanze in cui il tipo di un parametro
non può essere stabilito a priori.
La
tipica situazione in cui si avverte l'esigenza di un
meccanismo di polimorfismo parametrico è quando
si utilizzano le Collection Java, una libreria di contenitori
di oggetti generici:
Vector
list = new Vector();
list.add("Elemento 1");
list.add("Elemento 2");
Il
problema nasce quando si desidera recuperare un elemento
da una Collection: i metodi di interrogazione infatti
restituiscono oggetti del tipo generico Object, e per
attribuire all'oggetto il suo tipo originario è
necessario ricorrere ad un'operazione di casting:
String
s = (String)list.get(0);
Se
per errore si inserisce in una Collection un oggetto
del tipo sbagliato, lo sbaglio verrà individuato
solamente in fase di esecuzione, nel momento in cui
l'istruzione viene valutata:
Vector
list = new Vector();
...
list.add(new Integer(187));
String s = (String)list.get(0); // ClassCastException
Grazie
ai Generics è ora possibile dichiarare in fase
di creazione cosa si desidera inserire in una Collection,
in modo da impedire alla radice un possibile un uso
scorretto:
Vector<String>
list = new Vector<String>();
list.add("Elemento");
String s = list.get(0); // Non è più necessario
ricorrere al casting
In
fase di creazione viene assegnato in maniera esplicita
il tipo di dato manipolato dalla Collection, eliminando
la necessità di ricorrere al casting. A questo
punto è possibile identificare eventuali usi
scorretti in fase di compilazione, eliminando il problema
delle ClassCastException a Runtime:
Vector<String>
list = new Vector<String>();
list.add(new Integer(12)); // Errore di compilazione!
L'eliminazione
dell'operazione di cast porta dei vantaggi immediati
sulla leggibilità del codice.
Si osservi il seguente frammento di codice:
((String)list.get(0)).append("****");
Grazie
ai Generics, esso può essere riscritto nel modo
seguente:
list.get(0).append("****");
L'uso
di una API generica non presenta a prima vista particolari
difficoltà: l'utente deve semplicemente dichiarare
il tipo utilizzato ricorrendo alla notazione <Tipo>.
D'altra parte la creazione di una classe generica presenta
di sicuro maggiori problemi, che accenneremo nel prossimo
paragrafo.
Dichiarazioni di classi e metodi parametrici
Se si desidera creare una classe generica è necessario
specificare nella dichiarazione di classe il nome simbolico
del tipo all'interno dei delimitatori < e >. La
classe Vector, ad esempio, può essere definita
nel modo seguente:
public
class Vector<E> {
...
}
A
questo punto il simbolo E può essere usato all'interno
del sorgente come se fosse un normale identificatore
di tipo:
public class Vector<E> {
public void add(E x) {
...
}
public E get(int i) {
...
}
}
Il
simbolo E denota un tipo parametrico: il suo valore
verrà stabilito solo al momento della creazione
di istanze concrete dell'oggetto. Non esiste un limite
al numero di parametri generici, come si vede nel seguente
esempio:
public
class Coppia<A, B> {
private A primo;
private B secondo;
public Coppia (A primo, B secondo) {
this. primo = primo;
this. secondo = secondo;
}
public
A getPrimo() {
return primo;
}
public
B getSecondo() {
return secondo;
}
}
La
classe Coppia fa uso di due tipi parametrici, rappresentati
dai simboli A e B. In fase di creazione sarà
ovviamente necessario specificare il tipo dei parametri,
cosa che può essere fatta anche in modo implicito,
senza ricorrere alla notazione <Tipo>, attraverso
il passaggio di parametri al costruttore:
Coppia<String,
int> = new Coppia("Una Stringa",105);
Una
classe come questa torna utile ogni qualvolta si debba
dichiarare un metodo che restituisce una coppia di oggetti:
public
Coppia<String, int> getValues() {
return new Coppia(this.nome,this.età);
}
L'uso
avanzato dei tipi generici è abbastanza complesso.
In una classe parametrica è possibile dichiarare
metodi che a loro volta dipendono dal tipo specificato
in fase di creazione. La classe Vector, ad esempio,
possiede un metodo removeAll() che permette di rimuovere
un gruppo di elementi dalla collection. Tale metodo
richiede il passaggio di una collection contenente elementi
dello stesso tipo, pertanto la firma del metodo dovrà
specificare la cosa:
public
boolean removeAll(Collection<E> c);
E'
possibile estendere i limiti di un tipo parametrico,
ad esempio dichiarando un metodo che accetta Collection
di qualunque sottoclasse di E, compreso E stesso:
public
boolean addAll(Collection<? extends E> c)
Queste
ed altre funzionalità dei Generics sono molto
potenti, ma anche piuttosto macchinose: è chiaro
che pertanto che servirà un certo tempo per imparare
a sfruttarle nel migliore dei modi.
Autoboxing
/ Unboxing
La presenza di tipi primitivi è una caratteristica
importante del linguaggio Java, dato che permette di
operare in modo semplice ed intuitivo sulle unità
di informazione più elementari, come i tipi numerici
e i caratteri. D'altra parte la dicotomia Oggetti/Tipi
Primitivi comporta degli svantaggi pratici ogni qualvolta
si voglia dichiarare un metodo potenzialmente capace
di operare su entrambi. Ancora una volta le Collection
costituiscono un esempio lampante di questo tipo di
problema: fino ad ora l'unico modo per inserire valori
primitivi all'interno di una collection, era necessario
ricorrere alle apposite classi wrapper:
Vector
v = new Vector();
v.add(new Integer(10));
v.add(new Integer(15));
Il
ricorso alle classi wrapper costringe a ricorrere ad
una sintassi macchinosa nel momento in cui di vuole
recuperare dalla Collection il valore originario:
int
i = ((Integer)v.get(0)).intValue();
A
partire dal Java 1.5 è possibile utilizzare i
tipi primitivi in tutti i contesti in cui è permesso
l'uso di Object: le conversioni tra tipi primitivi e
wrapper type viene svolta in modo automatico dal compilatore.
Vector
v = new Vector();
v.add(10);
int i = (int)v.get(0);
Ovviamente
l'Autoboxing / Unboxing e presenta ulteriori vantaggi
se usato congiuntamente ai Generics:
Vector
<Integer> v = new Vector <Integer>();
v.add(12);
v.add(15);
int i = list.get(0);
In
conclusione, l'Autoboxing / Unboxing è una funzionalità
comoda, che non presenta particolari difficoltà
concettuali e che pertanto non dovrebbe causare grossi
problemi in fase di adozione.
Costrutto for di tipo "foreach"
Liste e vettori sono i contenitori di dati più
usati, e per questa ragione è molto facile trovare
in nei programmi frammenti di codice che processano
liste in questo modo:
int[]
numeri = new int[1000];
....
int totale;
for(int i=0;i< numeri.length;i++)
totale = toale + array[i];
Il
framework Collection prevede l'uso di un appositor oggetto
Iterator:
Vector<String>
v = new Vector<String>();
.
Iterator<String> i = v.iterator();
While(i.hasNext())
System.out.println(i.next());
Molti
linguaggi, tra cui ad esempio il Visual Basic, dispongono
di un'apposita keyword "foreach" che permette
di iterare tra gli elementi di una lista ricorrendo
ad una formulazione del tipo foreach(element in list).
I progettisti di Java hanno inserito un costrutto simile
come variante del ciclo for. Il risultato è che
ora è possibile riscrivere gli esempi precedenti
in questo modo:
int[]
numeri = new int[1000];
....
int totale;
for(int n : numeri)
totale = toale + n;
Vector<String>
v = new Vector<String>();
.
Iterator<String> i = v.iterator();
for(String s : v)
System.out.println(s);
Il
nuovo ciclo for richiede di specificare una variabile
a cui verrà assegnato ad ogni iterazione l'elemento
corrente, e un reference ad un vettore o ad una Collection
separati dal simbolo ":".
L'utilità
di questo costrutto è indiscutibile, ma rimane
il dubbio se non sarebbe stato opportuno introdurre
un'apposita keyword "foreach", in modo da
rendere il codice più leggibile. D'altra parte
è vero che il costrutto foreach canonico avrebbe
richiesto anche l'uso della keyword "in",
già utilizzata in migliaia di programmi come
identificatore di variabile. E' pertanto probabile che
in ultima istanza sia prevalsa la linea conservatrice,
per ridurre al minimo le incompatibilità verso
codice sorgente già esistente.
Annotazioni
Le annotazioni sono valori user defined che possono
essere abbinati a classi, interfacce, metodi e attributi
in modo da permettere successive elaborazioni da parte
di ambienti di sviluppo, compilatori o tool di manipolazione
del codice. Le annotazioni vengono definite "Metadati",
dal momento che si tratta di dati che descrivono altri
dati (in questo caso gli elementi del programma).
Le
annotazioni sono in pratica una sorta di infrastruttura
che gli sviluppatori di tool per la manipolazione di
codice possono utilizzare per memorizzare informazioni
utili direttamente nel codice sorgente, invece che in
files dal formato proprietario. Vediamo un esempio di
annotazione:
public
@remote void call() {
.
}
L'annotazione
vera e propria è un identificatore preceduto
dal simbolo @, situato nella firma di una classe, un'interfaccia,
un attributo o un metodo. Le annotazioni possono anche
contenere dei parametri tra parentesi:
@debug(devbuild=production,counter=1)
public void testMethod() {
}
I
valori contenuti nelle annotazioni vengono memorizzati
nel codice della classe, e pertanto possono essere letti
e manipolati anche a Runitme con i meccanismi per la
reflection:
ClasseAnnotata
c = new ClasseAnnotata();
try {
Annotation[] a =c.getClass().getMethod("metodoAnnotato").getAnnotations();
for (int i=0; i<a.length ; i++) {
System.out.println("a["+i+"]="+a[i]+"
");
}
}
catch(NoSuchMethodException e) {
System.out.println(e);
}
Un
tool può utilizzare le annotazioni per generare
codice parametrizzato, o per eseguire operazioni particolari
durante il debugging. Le annotazioni, ad esempio, possono
semplificare la programmazione di Java Beans o EJB,
dove le classi devono contenere codice ripetitivo legato
al particolare framework applicativo.
Inutile
nascondere che tra tutte le novità di questa
edizione, le annotazioni sono certo la cosa che lascia
più perplessi, non per la sua potenziale utilità,
ma per il fatto che è difficile immaginare che
un suo uso intensivo possa rendere il codice più
comprensibile.
Import Statico
Nelle attuali versioni di Java si ricorre a variabili
static final per definire costanti. Per accedere alle
variabili statiche è necessario utilizzare la
notazione puntata sull'identificatore della classe,
ad esempio:
//
Il volume della sfera qual'è? Quattro terzi pigreco
r tre
double volumeSfera = 4/3 * Math.PI * raggio^3;
Le
costanti sono molto usate all'interno delle API del
JDK; si osservi ad esempio un frammento di codice che
fa uso dell'API grafica Swing:
Jpanel
p = new Jpanel();
p.setLayout(new BorderLayout());
p.add(BorderLayout.NORTH,p1);
p.add(BorderLayout.CENTER,p2);
p.add(BorderLayout.SOUTH,p3);
L'import
statico permette di importare nel namespace di un sorgente
metodi e attributi statici di una classe. Se ad esempio
si desidera poter usare all'interno di un sorgente le
funzioni matematiche (sin() , cos(), abs() ecc) come
se fossero metodi della classe di lavoro, sarà
ora sufficiente scrivere nell'intestazione di un sorgente
la seguente istruzione:
Import
static java.lang.Math.*;
Allo
stesso modo, l'istruzione:
import
static java.awt.BorderLayout.*;
permetterà
di riscrivere l'esenpio precedente in questo modo:
p.add(NORTH,p1);
p.add(CENTER,p2);
p.add(SOUTH,p3);
Si
noti che è possible importare anche singoli membri,
come un singolo attributo o metodo:
import
static java.lang.Math.PI;
double volumeSfera = 4/3 * PI * raggio^3;
L'unica
critica che può eventualmente essere mossa a
questo costrutto è che esso produce una grossa
espansione dello spazio dei nomi, simile a quella prodotta
dalla direttiva include presente in C e C++. L'abituale
sintassi di chiamata a metodi statici è certamente
verbosa, ma d'altra parte permette di identificare con
precisione l'oggetto di una chiamata. Un'uso eccessivo
di import statici potrebbe in definitiva creare confusione
in fase di manutenzione dei sorgenti.
Typesafe
Enums
Le enumerations sono insiemi di oggetti dotati di proprietà
comuni; ogni oggetto è presente in memoria in
un'unica istanza, e viene identificato attraverso un
nome simbolico. In altre parole, le Enumerations sono
insiemi di identificatori che denotano un tipo logico.
Un
esempio tipico di enumerazione è l'elenco dei
giorni della settimana: Lunedì, Martedì,
Mercoledì, Giovedì, Venerdì, Sabato,
Domenica. Altri esempi sono i colori dell'arcobaleno
(Rosso, Arancio, Giallo, Verde, Blu, Indaco, Viola)
i segni cardinali (Nord, Sud, Est, Ovest) e le stagioni
(Autunno, Inverno, Primavera, Estate).
Dal
momento che l'iterazione è uno dei fondamenti
dell'automazione, non deve stupire che l'iterazione
su insiemi logici possa tornare utile nella programmazione.
Vediamo alcuni esempi:
- Esegui
una certa operazione per ogni giorno della settimana
- Assegna
ad auto della tua collezione un nome caratterizzato
da uno dei colori dell'arcobaleno scelto a rotazione,
seguito da un numero progressivo
- Associa
ad ogni sport le stagioni in cui può essere
praticato
- Componi
un bouquet di fiori usando quelli disponibili
Fino
ad ora la tecnica di rappresentazione delle enumerations
dipendeva dall'utente. La tecnica più usata prevede
il ricorso a costanti numeriche, come nel seguente esempio:
public
class Settimana {
public static final int LUNEDI = 0;
public static final int MARTEDI = 1;
}
Il
codice client accede ai valori statici attraverso la
notazione puntata:
appuntamento.setDay(Settimana.LUNEDI);
Questa
tecnica di programmazione è semplice da capire
e da implementare, ma presenta d'altra parte numerosi
difetti:
- Non
è tipizzata: per essere sicuri che il chiamante
passi un valore corretto è necessario un controllo
esplicito sul valore numerico
- I
nomi delle costanti appartengono al namespace di una
particolare classe, e per essere utilizzati richiedono
il ricorso alla notazione puntata
- Il
valore delle costanti viene compilato nel codice client.
In questo modo si presenta una dipendenza pericolosa
tra la classe che fornisce il servizio e quelle che
lo utilizzano: se per qualche ragione i valori delle
costanti devono essere cambiati, è necessario
ricompilare tutte le classi che ne fanno uso.
- Il
valore reale del parametro non contiene informazioni
stampabili.
Il
costrutto enum permette di definire un insieme in questo
modo:
public
enum GiorniDellaSettimana { Lunedì, Martedì,
Mercoledì, Giovedì, Venerdì, Sabato,
Domenica};
Una
variabile di tipo GiorniDellaSettimana può assumere
unicamente uno dei valori specificati. Il controllo
di appartenenza viene effettuato dal compilatore, e
pertanto viene garantita la consistenza. Gli elementi
di una enum sono oggetti: pertanto è possibile
inserirli in una collection, stamparli, usarli come
indici per una Hashtable, serializzarli ed usarli in
un costrutto switch - case. Ogni enum presenta un attributo
VALUES che contiene tutti i possibili valori, in modo
da permettere l'iterazione. E' anche possibile, ricorrendo
ad una sintassi un po' più complessa, definire
una enum i cui elementi posseggono metodi ed attributi:
//
Trenta giorni ha Novembre, con April, Giugno e Settembre
public enum Mesi {
gennaio(31), febbraio(28), marzo(31), aprile(30),maggio(31),giugno(30),luglio(31),
agosto(31),settembre(30),ottobre(31),novembre(30),dicembre(31);
private final int giorni; // attributo
Coin(int giorni) { // costruttore
this. giorni = giorni;
}
public int getGiorni() { // metodo
return giorni;
}
}
In
questo esempio l'enum Mesi denota un tipo caratterizzato
dai valori gennaio, febbraio, marzo, aprile, maggio,
giugno, luglio, agosto, settembre, ottobre, novembre,
dicembre. Ciascun elemento della enumerazione è
a sua volta caratterizzato da un differente valore dell'attributo
"giorni", che può essere letto attraverso
il metodo getGiorni():
public
static void main(String[] args) {
for (Mesi m : Mesi.VALUES)
System.out.println("Il mese" + m + "ha
"+m.getGiorni()+" giorni");
}
Da
un punto di vista estetico il risultato è notevole:
le enum permettono di definire, con una unica dichiarazione,
una classe e l'insieme di tutte le sue possibili istanze
che, è bene ricordare, nel caso di enumerazioni
ha cardinalità finita. Se usate in contesti adeguati,
le enum non dovrebbero presentare alcun problema al
programmatore.
Varargs
L'ultima novità di Java 1.5 è la possibilità
di dichiarare metodi che accettano un numero variabile
di argomenti. Attualmente l'unico modo di passare un
numero variabile di argomenti ad un metodo è
attraverso un vettore:
public
void stampa(String[] stringhe) {
for(int i=0;i<stringhe.length ; i++)
System.out.println(stringhe[i]);
}
L'aspetto
più macchinoso di questo sistema è la
formulazione della chiamata: prima di effettuare la
chiamata vera e propria è infatti necessario
inizializzare in modo esplicito il vettore degli argomenti:
String[]
arguments = { "La","Vispa","Teresa","Avea","Tra","L'Erbetta"};
stampa(arguments);
Su
Java 1.5 è invece possibile riscrivere il metodo
stampa in questo modo:
public
void stampa(String
stringhe) {
for(String s : stringhe)
System.out.println(s);
}
A
differenza dell'esempio precedente, l'array viene definito
in modo implicito attraverso l'identificatore "...".
Il codice client ora può chiamare il metodo stampa()
specificando gli argomenti direttamente nella chiamata:
stampa("La","Vispa","Teresa","Avea","Tra","L'Erbetta");
Si
noti che il metodo così definito accetta un qualunque
numero di argomenti:
stampa("Uno");
stampa("Uno","Due","Tre");
stampa("Uno","Due","Tre","Quattro","Cinque","Sei","Sette");
....
L'aggiunta
di dei parametri variabili ha permesso di implementare
una funzione printf() del tutto simile all'analoga funzione
C:
System.out.printf("Lo
studente %s ha totalizzato %5 punti.d\n", student,rating);
In
questo modo è possibile effettuare un porting
di codice legacy scritto in C mantenendo il formato
di visualizzazione esistente. Nel complesso si tratta
di un'aggiunta interessante, realizzata in modo pulito:
per questo motivo non dovrebbero sorgere problemi in
fase di adozione.
Conclusioni
L'uscita del nuovo J2SDK è destinata ad avere
un grosso impatto sulla comunità degli sviluppatori,
data la portata trasversale delle nuove features. Per
dare un'idea di quanto profondo sia il cambiamento in
atto, basti pensare che la classe java.lang.Class, una
delle classi più importanti del linguaggio, è
ora generica, e che questa scelta condiziona (sicuramente
in meglio) tutte le funzionalità legate alla
reflection. D'altra parte è innegabile che da
oggi la curva di apprendimento di Java sarà un
po' più ripida: solo col tempo potremo sapere
se questa scelta non andrà ad influenzare in
modo negativo la diffusione di Java nel mondo.
Bibliografia
[1] http://java.sun.com/j2se/1.5.0/download.jsp
[2] http://weblogs.java.net/pub/wlg/261
[3] http://pizzacompiler.sourceforge.net/
[4] http://www.cis.unisa.edu.au/~pizza/gj/
[5] http://jcp.org/en/jsr/detail?id=014
[6] http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
|