 |
[- JAVA 1.5: NOME IN CODICE
TIGER -] (data 16/06/2003)
di : [-SEGO-] segoXtechtown.it (sostituite la X con @)
DISCLAIMER
Questo documento può essere liberamente copiato e distribuito da chiunque,
ma a nessuno è permesso di cambiarlo in alcun modo. Ogni copia o documento
derivato dovrà riportare l'indicazione alla fonte.
Qualsiasi commento o suggerimento è benvenuto
e può essere inviato al seguente indirizzo: segoXtechtown.it (sostituite
la X con @). Eventuali correzioni di questa pubblicazione saranno disponibili
sul sito internet http://www.techtown.it
INDICE
- Introduzione
- La situazione attuale
- ... Verso J2SE 1.5
- PARTE I: I Generics
- PARTE II: Ciclo For
migliorato
- PARTE III: Autoboxing/Unboxing
- PARTE IV: TypeSafe
Enums
- PARTE V: Import statico
- PARTE VI: Metadata
INTRODUZIONE
Come potete vedere dal titolo quella di oggi sarà un breve trattazione
della nuova SDK a cui Sun sta da tempo lavorando e che ha intenzione di
rilasciare verso la fine di quest'anno.
Innanzitutto parto subito col dire che questo articolo ho deciso di scriverlo
dopo aver letto una intervista sul numero 71 di IOProgrammo (luglio/agosto
2003). Questa intervista è in realtà la traduzione fatta da Raffaele del
Monaco dell' originale intervista di Janice J.Heiss a Joshua Bloch.
Per farla breve Joshua Bloch ha esposto le caratteristiche salienti di
quella che sarà la nuova release di casa Sun. Ha parlato delle novità
più importanti, esponendole anche con esempi di codice.
Visto che per scrivere un qualsiasi documento decente non ci si può fermare
ad una sola fonte, ho cercato un po' in rete e ho trovato un altro po'
di materiale che vi elenco qui sotto:
- "A conversation with Joshua Bloch"
URL: http://java.sun.com/features/2003/05/bloch_qa.html
L'intervista originale al Senior Staff Engineer di Java Software:
utile per chi non ha IOProgrammo.
- "From Mantis to Tiger"
URL: http://java.sun.com/features/2002/03/totiger.html
Breve riassunto delle caratteristiche della J2SE 1.4 e quelle
che erano le prospettive di sviluppo per la J2SE 1.5
- "Java 1.5: siate più precisi, cioè più generici"
URL: http://pcpro.mytech.it/pcpro/know_how/art006001038883.jsp
Articolo sul sito di PC Professionale: si concentra principalmente
sulla novità dei Generics.
- "Importing Static Members in the Java Programming Language"
URL: http://jcp.org/aboutJava/communityprocess/jsr/tiger/static-import.html
Articolo che tratta della novità dell'import statico.
- "An enhanced for loop for the Java Programming Language"
URL: http://jcp.org/aboutJava/communityprocess/jsr/tiger/enhanced-for.html
Articolo che tratta della novità del ciclo for migliorato.
- "Autoboxing support for the Java Programming Language"
URL: http://jcp.org/aboutJava/communityprocess/jsr/tiger/autoboxing.html
Articolo che tratta della novità dell'autoboxing/inboxing.
- "A Typesafe Enum Facility for the
Java Programming Language"
URL: http://jcp.org/aboutJava/communityprocess/jsr/tiger/enum.html
Articolo che tratta della novità dei
typesafe enums.
- "Preparing for Generics: an introduction"
URL: http://developer.java.sun.com/developer/technicalArticles/releases/generics/
Articolo che tratta della novità più importante: i Generics.
- "Effective Java Programming Language Guide" di Joshua
Bloch (in particolare il cap.5 item 21)
URL: mmm.....piccolo suggerimento: cercate bene!!
Una guida ben fatta che introce in maniera semplice e funzionale
al linguaggio Java. Ottima per i neofiti, ma ricca di spunti anche per
coloro che conoscono il Java.
Da sottolineare la parte in cui viene spiegato come "convertire"
i tipici costrutti C in linguaggio Java.
NOTA: a meno che non sia
espressamente indicato i pezzi di codice sono presi dalle varie fonti.
LA SITUAZIONE ATTUALE
Prima di passare a vedere quali saranno queste novità (che di certo alcuni
faranno un po' di fatica a digerire) vediamo come si presenta la situazione
al momento. Se non sbaglio ora come ora, l'ultima release stabile della
piattaforma J2SE è la 1.4.2. Quando mi sono affacciato al mondo Java,
ossia due anni fa circa, la versione utilizzata per programmare era la
1.3.1. Ricordo inoltre che nella primavera del 2002 fece la sua comparsa
la versione 1.4 di Java che portava con se migliorie molto importanti,
quali:
- un nuovo package per l'I/O (java.nio)
- espressioni regolari (java.util.regex)
- socket e connessioni sicure attraverso i protocolli
SSL e TLS (java.net.ssl)
- pieno supporto a XML e XSLT (esempio Java API for XML
Processing o JAXP)
- asserzioni (nuova parola chiave assert): un nuovo meccanismo
di debugging e di check. In pratica nel codice il programmatore inserisce
delle espressioni del tipo "assert ‹espressione booleana›".
Nel caso a runtime questa venga rispettata (sia uguale a true) allora
tutto fila liscio (perchè in pratica le "supposizioni" erano giuste)
altrimenti (uguale a false) viene lanciata una bella eccezzione di tipo
AssertionError.
- migliorie alla libreria grafica Java2D
- package in grado di fornire funzionalità di logging
(java.util.logging)
....tanto per citarne alcune.
Questo per far capire cosa? come il passaggio da una release all'altra
significhi cambiamento, miglioramento, maggiori qualità e prestazioni
in termini di efficienza e scalabilità. Basti pensare come alcuni test
interni condotti dal team di sviluppo hanno evidenziato come nell'utilizzo
di applicazioni client basate su J2SE 1.4 ci sia stato un incremento di
performance compreso tra il 20%-80% rispetto ad applicazioni basate sulla
versione 1.3.1.
Il senso di tutto questo discorso è di preparare chi legge all'idea che,
anche se nella versione 1.5 ci saranno dei cambiamenti che ad alcuni potranno
sembrare radicali (i generics in primis) alla fine guardando bene ci si
rende conto dell'importanza degli stessi in termini di pulizia del codice
e ottimizzazione delle prestazioni.
Dico questo perchè quando la prima volta(molto superficialmente) ho letto
l'intervista ho avuto come l'impressione che alcune di queste novità mi
avrebbero costretto a cambiare il mio modo di scrivere codice (niente
più casting, uso di informazioni aggiuntive nelle dichiarazioni per l'uso
dei generics). Poi rileggendo...capendo gli esempi di codice... ho capito
l'importanza che avranno!
... VERSO JAVA 2 STANDARD EDITION 1.5
Dopo una "pesante" ma a mio modo di vedere necessaria introduzione, passiamo
a una sorta di riassunto dell'intervista, analizzando le novità che vedranno
protagonista TIGER (così viene chiamata la nuova release )
- OBIETTIVI: miglioramenti alla piattaforma per ottenere
programmi più corti, robusti, chiari, meno soggetti a bug.
- SOLUZIONE: implementare in java costrutti linguistici
che sostituiscano parti di codice e costrutti tipicamente usati dai
programmatori.
- RISULTATI: codice più pulito e meno soggetto a errori,
possibilità di evitare alcuni tipi di errore a runtime già a tempo di
compilazione.
In breve vengono ora elencati i miglioramenti fondamentali
(li analizzeremo dettagliatamente più avanti):
1. Generics: in grado di fornire meccanismi di controllo a tempo di compilazione
sulle Collection, evitando le fastidiose operazioni di casting.
2. Ciclo For migliorato: permette di usare gli iteratori all'interno del
ciclo senza dover scrivere il codice necessario a gestirli (come invece
siamo stati abituati a fare finora).
3. Autoboxing/unboxing: conversione automatica tra tipi primitivi e tipi
wrapped (utilizzabili nelle Collection).
4. Typesafe enums: nella loro forma più semplice, a prima vista dei semplici
enum stile C/C++. In realtà sono dei meccanismi molto più potenti.
5. Import statico: evita di indicare per ogni membro statico la classe
a cui appartengono.
6. Metadata: il supporto per i metadati permette di sfruttare tool che
generano codice in maniera automatica.
PARTE I: I Generics
Questa secondo Joshua Bloch rappresenta la novità più importante...e davvero
penso sia difficile dargli torto. Lo scopo principale dei Generics come
dicevamo è quello di modificare in positivo il nostro modo di scrivere.
Esempio di codice senza generics: List words = new ArrayList();
Esempio di codice con generics: List‹String› words = new ArrayList‹String›();
Certo la cosa a prima vista può sembrare strana e a chi conosce il C++
può ricordare i Template: il bello però di questo meccanismo è che ci
permetterà di eliminare la necessità di effettuare i casting lungo tutto
il codice quando estraiamo degli elementi da una Collection, come una
LinkedList o una HashTable...e non vedremo più l'applicazione andare in
crash in seguito ad un ClassCastException! Vi sembra poco tutto questo???
Infatti, se quando scriviamo il codice dichiariamo che vogliamo che quella
determinata lista contenga stringhe, allora tutte le volte che la utilizzeremo
dovremmo inserire unicamente stringhe, e al tempo stesso saremo certi
di estrapolare dalla stessa unicamente degli oggetti di tipo String! Nel
caso per qualche motivo lungo il codice dell'applicazione, si tenti di
inserire qualcosa che non sia una stringa, l'errore ci viene segnalato
a tempo di compilazione e non a runtime come avviene ora mediante una
fastidiosissima ClassCastException.
Vediamo ora un esempio di come spariranno i cast.
Esempio di codice senza generics: String title=((String) words.get(i)).toUppercase();
Esempio di codice con generics: String title=words.get(i).toUppercase();
La novità Generics tuttavia non riguarda solo i parametri o le variabili...Si
possono dichiarare anche classi, interfacce e metodi Generic!Date uno
sguardo alla tabella sottostante:
| |
Syntax |
| Paramaterized Type |
Vector‹String› stringVector = new Vector‹String›
List‹Integer› integerList = new List‹Integer›
|
| Interface |
interface List‹Element› implements MyInterface{...} |
| Class |
class MyList‹Element› {...}
class MyList‹Element› implements List {...} |
| Method |
boolean containsBoth(Element a, Element b);
static ‹Element› boolean swap(List‹Element› list,
int i, int j); |
Spulciando tra le fonti trovate più info e più esempi (codice) inoltre troverete
anche le indicazioni su come cominciare a far pratica con i generics, utilizzando
un prototipo di quella che sarà l'implementazione dei Generics.
PARTE II: Ciclo For migliorato
Volete che vi dica in due parole di cosa si tratta??In un certo senso anche
java avrà il costrutto particolare "foreach", che magari a qualcuno ricorda
il bash scripting o qualche altro linguaggio di scripting.
Nonostante questo, la scelta degli sviluppatori è stata quella di non includere
due nuove keywords come "foreach" e "in", ma hanno scelto di modificare
leggermente la struttura del costrutto.
Come dicevamo all'inizio il ciclo for migliorato ci permetterà di risparmiare
tempo, visto che della parte relativa l'iterazione su una qualsiasi Collection,
se ne occupa il compilatore.
Passiamo subito ad un esempio di codice:
Esempio di normale utilizzo di un iteratore:
void cancelAll(Collection c) {
for (Iterator i = c.iterator(); i.hasNext(); ) {
TimerTask tt = (TimerTask) i.next();
tt.cancel();
}
}
Esempio precedente usando il "for migliorato":
void cancelAll(Collection c) {
//notate come è cambiato il for. NOTA: Si legge "Object o in c".
for (Object o : c)
((TimerTask)o).cancel();
}
Molta meno confusione e tutto più automatico....
Tra l'altro nell'esempio sopra facciamo ancora uso dei cast...ma se avessimo
usato i generics...ecco il risultato:
void cancelAll(Collection c) {
for (TimerTask task : c)
task.cancel();
}
Che ne dite???non male vero!
PARTE III: Autoboxing/Unboxing
Molto spesso scrivendo codice capita di dover passare da tipi primitivi
a riferimenti ad oggetti.
Tanto per fare degli esempi, passare da int a Integer, da boolean a Boolean,
da long a Long e viceversa.
Il lato negativo della cosa è che oltre a dover scrivere codice in più per
passare dall'uno all'altro, il problema con i tipi primitivi è che non possono
essere direttamente inseriti all'interno delle Collection: siamo quindi
per forza di cose costretti ad usare i tipi wrapped. Ad esempio fare un:
new Integer(valoreIntero);
Con i meccanismi del boxing (da primitivo a wrapped) e dell'unboxing (da
wrapped a primitivo) le conversioni avvengono in maniera automatica eliminando
così una notevole seccatura al programmatore.
Un problema ancora aperto in questo discorso rappresenta il caso "null".
Cosa fare quando si tenta di applicare questi meccanismi su un riferimento
a null??? Gli sviluppatori stanno valutando se convertire in maniera automatica
null con zero oppure se far lanciare un NullPointerException. E' chiaro
che ciascuna delle due scelte avrebbe i suoi risvolti positivi e negativi...ma
la mia personale idea è che una automatica conversione di null a zero sarebbe
decisamente più problematica e introdurebbe più errori del fatto di dover
gestire un NullPointerException. Voi che ne pensate???
PARTE IV: TypeSafe Enums
Per chi non lo sapesse mentre in C esiste il costrutto enum, questo è stato
volutamente escluso da Java per vari motivi.
In C infatti è perfettamente naturale fare una cosa del genere:
typedef enum { MELA, ARANCIA, BANANA, ALBICOCCA } frutti;
typedef enum { SEDIA, BANCO, DIVANO, TAVOLO } mobili;
frutti daMangiare = MELA;
ma purtroppo anche una cosa del genere è perfettamente compilabile!!
frutti fruttoSconosciuto = BANANA * ARANCIA / (SEDIA - ALBICOCCA)
La prima cosa che balza agli occhi è infatti la confusione logica creata
da un'istruzione simile!!
In C infatti quando si usano gli enum, non si fa altro che creare un insieme
di costanti numerate (interi): il costrutto infatti non fornisce alcun controllo
dei tipi, ed è per questo che possiamo scrivere una schifezza simile a quella
sopra!
Visto che manca...come possiamo creare nella maniera più semplice possibile
un sostituto per il costrutto enum????
La risposta è in questo pezzo di codice:
public class Frutti {
public static final int FRUTTO_MELA = 0;
public static final int FRUTTO_ARANCIA = 1;
public static final int FRUTTO_BANANA = 2;
public static final int FRUTTO_ALBICOCCA = 3;
}
Questa soluzione viene detta Int Enum....il perchè
mi sembra chiaro: cosa c'è all'interno della classe??!!
In ogni caso se avrete occasione di vedere il libro (o il .pdf :-) ) di
Joshua Bloch potrete notare come egli proponga una soluzione più complicata,
ma più potente per creare una alternativa chiamata Typesafe Enum. Il perchè
lo si intuisce anche questa volta dal nome: l'importanza del controllo sui
tipi!
Non starò qui a spiegare l'implementazione che ha fatto Joshua Bloch (consiglio
di andarla a vedere sul libro, è molto istruttiva) perchè porterebbe via
troppo tempo, mi limiterò a dirvi che ha in un certo senso anticipato quello
che ci sarà nella versione Tiger.
Il meccanismo Typesafe Enums avrà le seguenti caratteristiche:
- controllo sulla correttezza dei tipi a tempo di compilazione
- performance simili a quelle ottenibili usahdo gli int
enum (semplici costanti intere)
- ogni tipo enum avra il proprio namespace (spazio dei
nomi), in questa maniera potremmo avere nomi di costanti uguali, l'importante
è che appartengano a namespace diversi.
Questa cosa non può essere fatta col tipico enum in C.
- i tipi enum potranno essere usati nelle Collection (HashMap,
LinkedList e così via) dato che sono oggetti a tutti gli effetti.
- potremmo aggiungere campi e metodi in maniera arbitraria:
i tipi enum non sono altro che classi!
- per lo stesso motivo di sopra, potranno implementare
interfaccie
A dire il vero la maggior parte di queste caratteristiche
sono presenti appunto nella soluzione (di cui parlavamo sopra) contenuta
nel libro di Joshua Bloch.
Allora cosa c'è di nuovo????Il fatto che mentre fino ad ora per implementare
un typesafe enums in maniera corretta c'è stata la necessità di scrivere
un bel po' di codice (motivo principale per cui molti ripiegano tuttora
verso i semplici int enum) con questa nuova feature sarà tutto "automatizzato":
il costrutto proposto infatti è molto semplice.
Infine altra caratteristica importante è che potrà essere tranquillamente
usato negli switch.
Dopo uno sguardo al codice riportato qui sotto (preso dall'intervista) sarà
tutto più chiaro:
Rappresentazione delle monete disponibili in America:
public enum Coin {
penny(1), nickel(5), dime(10), quarter(25);
Coin(int value) { this.value = value; }
private final int value;
public int value() { return value; }
}
NOTE:
1. Nelle dichiarazioni di costanti viene invocato un costruttore
passando un int.
2. Il valore viene memorizzato in un campo privato.
3. Il valore può essere recuperato grazie ad un metodo pubblico
value().
Classe di test che mostra come sfruttare l'oggetto enum sopra definito:
public class CoinTest {
public static void main(String[] args) {
for (Coin c : Coin.VALUES)
System.out.println(c + ": \t" + c.value() +"¢ \t" + color(c));
}
private enum CoinColor { copper, nickel, silver }
private static CoinColor color(Coin c) {
switch(c) {
case Coin.penny: return CoinColor.copper;
case Coin.nickel: return CoinColor.nickel;
case Coin.dime:
case Coin.quarter: return CoinColor.silver;
default: throw new AssertionError("Unknown coin: " + c);
}
}
}
NOTE:
1. CoinColor è un altro oggetto enum: questa è la forma più
semplice di dichiarazione.
2. Abbiamo una costante nickel sia in CoinColor che in Coin:
questo grazie al fatto che ogni oggetto enum ha un proprio namespace.
3. Abbiamo usato il ciclo for migliorato per iterare tra le
costanti dell'enum Coin.
4. Da notare l'uso dello switch nella funzione color()
PARTE V: Import
statico
Lo scopo è anche in questo caso semplificare la vita al programmatore:
permettergli di utilizzare membri statici senza che ogni volta sia costretto
a dover indicare la classe a cui appartiene.
In sostanza viene introdotta una variante dell'import così come lo conosciamo:
il risultato finale sarà quello di poter usare la keyword import per importare
non solo classi o interfaccie (come facciamo ora) ma anche campi e metodi
statici.
Fino ad adesso infatti si specificava il nome della classe a cui il membro
apparteneva nella forma:
‹NOMECLASSE›.‹campoStatico› o ‹NOMECLASSE›.‹metodoStatico›
La nuova dichiarazione avrà la forma: import static TypeName.Identifier;
Questo consentirà all'atto della compilazione di importare nell'unità
di codice corrente il membro statico Identifier che appartiene alla classe/interfaccia
TypeName.
Altra dichiarazione che sarà possibile usare: import static TypeName.*;
Questo consentirà di importare tutti i membri statici(campi o metodi)
che appartengono alla classe/interfaccia Typename.
Un piccolo esempio sull'utilità di questa feature:
ora sia costretti a scrivere: Math.abs(x) Math.sqrt(x) Math.max(a,b)
in futuro potremmo scrivere: abs(x) sqrt(x) max(a,b)
PARTE VI: I
metadata
Avete mai usato i javadoc???
Il loro uso tipico è quello di produrre dei file html in stile documentazione
delle api dell'sdk, in cui siano ad esempio indicati i parametri del metodo,
l'autore della classe, cosa ritorna e cosa fa un metodo...e così via.
Insomma a partire da delle semplici note nei commenti si ottiene un output
molto più complesso e formattato.
Il principio del supporto per i metadati è in parole povere lo stesso:
mediante delle semplici note inserite all'interno del codice si otterrà
in output del codice già bello e pronto. Molto spesso infatti capita quando
si creano particolari componenti, di dover riscrivere delle parti di codice
che hanno sempre la medesima struttura, ma variano magari per il nome
della classe, dell'interfaccia o dei campi. In questa maniera sarà possibile
attraverso delle "dichiarazioni" dire cosa vogliamo, e il codice corrispondente
verrà generato. In sostanza si tratta di un meccanismo che comunicherà
con i vari tool di sviluppo, che si faranno carico di volta in volta di
generare il codice corrispondente, senza che il programmatore sia costretto
a scriverlo riga per riga.
Se vogliamo dirla tutta i tool di sviluppo integrano dei meccanismi per
creare codice in maniera automatica: basti pensare a Netbeans o Eclipse
tanto per citarne alcuni. Mediante ad esempio delle finestre, chiedono
magari di inserire il nome della classe, dei campi, se la classe estende
un particolare classe o implementa un'interfaccia.
In questa maniera, dopo aver raccolto tutte queste info, sono in grado
di produrre del codice corrispondente.
Per esempio può capitare, che la classe che vogliamo scrivere oltre ai
metodi che abbiamo specificati, ne contenga altri in considerazione del
fatto che implementa una particolare interfaccia.
Ormai quasi tutti i tool di sviluppo (degni di questo nome) permettono
di utilizzare meccanismi "automatizzati" per la generazione di codice.
Tuttavia con questo nuovo meccanismo la cosa sarà ancora più semplice
e immediata, perchè basterà inserire particolari dichiarazioni all'interno
del codice!
Ultimo aggiornamento effettuato domenica 31 ottobre 2004
|
|