Login
Cerca all'interno di JavaPortal
Help
Home Page Documentazione Forum Progetti Partner Pubblica!
Documentazione > Tutorial > Introduzione a Groovy
Modifica Impostazioni
Hide
Best Practices
EJB
Frameworks
Howto
J2EE
J2ME and Wireless
J2SE
JSP e Servlet
Java Application Server
Java IDE/Tools
Java Media
Java Security
Java Sys Admin
Java e XML
Java e SQL
OpenSource Java
Patterns
Repository
Tesi
UML
Web Services
Slide
White Paper di jws.it
project management
Eventi
Groovy

Hai una tesi in Java?
Tesine preparate
per esami?
Pubblica tutto su
JavaPortal!

Scrivi al nostro staff


Online le slide del Javaday IV


John Fitzgerald Kennedy
L'uomo è il computer più straordinario di tutti


Hello World con Eclipse


Rss Feed
Home Page
Articoli
News
Forum
Classi

 
Add to Shortcuts
Introduzione a Groovy

Il Blog di Fabio Collini: blog2j

Groovy è un linguaggio di programmazione di nuova concezione che può essere considerato una valida alternativa a java per diversi motivi:

  • usa una sintassi molto simile a quella java ma con alcuni costrutti ispirtati a python e ruby che ne semplificano l'utlizzo e che rendono il codice più facile da leggere e da mantenere
  • è un linguaggio dinamico ma può essere utilizzato anche con oggetti tipizzati
  • può essere usato semplicemente per creare script lanciabili da shell o da un task di ant
  • è basato sul jdk java, tutte le classi utilizzabili in java sono disponibili anche in groovy
  • il compilatore produce file .class compatibili con quelli generati dal compilatore java, per questo motivo nello stesso progetto possono coesistere (e interagire fra di loro) classi java e classi groovy


Uno sviluppatore java si troverà subito a suo agio con groovy e soprattutto non dovrà riiniziare da zero potendo usare una sintassi familiare, le stesse classi di base del linguaggio e perfino lo stesso ambiente di sviluppo.


Groovy nasce da una idea di James Strachan che per la prima volta ne parlò nel suo blog nell'agosto del 2003. L'idea è poi diventata la JSR 241 e nel giro di alcuni anni sono state rilasciate varie versioni. Il 2 gennaio 2007 è stata rilasciata la versione 1.0, il 9 dicembre 2007 la versione 1.5.

In questa introduzione vedremo come aggiungere ad un progetto java esistente alcune classi scritte in groovy.

Per scrivere il codice ho utilizzato Eclipse con installato il plugin per groovy, se avete necessità leggete l’ howto su come istallare il plugin Groovy su Eclipse.

Iniziamo analizzando un semplice esempio: un modulo di importazione esportazione di voci di una rubrica. Potete scaricare il codice sorgente completo, comprende 3 package: interfacce principali, implementazione delle interfacce in java e implementazione delle stesse interfacce in Groovy.

Le interfacce comuni sono:

  • due bean usati per racchiudere i dati


public interface Persona { 
 
    String getNome(); 
    void setNome(String nome); 
    String getCognome(); 
    void setCognome(String cognome); 
    Date getDataDiNascita(); 
    void setDataDiNascita(Date dataDiNascita); 
    Indirizzo getIndirizzo(); 
    void setIndirizzo(Indirizzo indirizzo); 
} 

 
public interface Indirizzo { 
 
    String getViaNumero(); 
    void setViaNumero(String viaNumero); 
    String getCitta(); 
    void setCitta(String citta); 
} 



  • una interfaccia da implementare per importare i dati


    public interface Importatore<T> { 
        List<T> importa(); 
    } 



  • una interfaccia da implementare per esportare i dati


public interface Esportatore<T> { 
    void esporta(List<T> l); 
} 

Iniziamo ad aggiungere un po' di Groovy, per aggiungere al class path le librerie necessarie basta cliccare col tasto destro sul progetto nel package explorer di eclipse e selezionare Groovy -> Add Groovy nature.
Adesso siamo pronti a scrivere la prima classe Groovy, per fare ciò usiamo il menù “New” di Eclipse e selezioniamo Groovy class. Andiamo a creare l'implementazione delle interfacce dei bean che contengono i dati:

class PersonaGroovyImpl implements Persona { 
    String nome 
    String cognome 
    Date dataDiNascita 
    Indirizzo indirizzo 
} 
 
class IndirizzoGroovyImpl implements Indirizzo { 
    String viaNumero 
        String citta 
    }

A prima vista sembra che manchi qualcosa... Invece c'è tutto! Analizziamo nei dettagli la classe IndirizzoGroovyImpl, la stessa classe java è la seguente:

public class IndirizzoJavaImpl implements Indirizzo { 
 
    private String viaNumero; 
    private String citta; 
 
    public String getViaNumero() { 
        return viaNumero; 
    } 
    public void setViaNumero(String viaNumero) { 
            this.viaNumero = viaNumero; 
        } 
        public String getCitta() { 
            return citta; 
        } 
        public void setCitta(String citta) { 
            this.citta = citta; 
        } 
    } 

4 righe di codice contro 18, scopriamo come è possibile:

  • la classe non è definita public infatti in groovy public è il comportamento di default per le classi
  •  groovy usa le properties, un campo senza definizione di visibilità (public, private o protected) è una property, non devono essere definiti i metodi get e set che vengono aggiunti in automatico
  • il punto e virgola a fine riga è opzionale


Grazie a queste caratteristiche la scrittura di bean è immediata e non ripetitiva, i bean sono semplicissimi da leggere e ogni informazione (nome dei campi, tipi) è scritta in un unico posto.

Proseguiamo con la classe che esegue l'importazione da file di testo, una implementazione java è

public class ImportatoreTxtJavaImpl implements Importatore<Persona> { 
 
    private final String fileName; 
 
    public ImportatoreTxtJavaImpl(String fileName) { 
        this.fileName = fileName; 
    } 
 
    private static SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); 
 
    public List<Persona> importa() { 
        List<Persona> l = new ArrayList<Persona>(); 
        BufferedReader reader = null; 
        try { 
            reader = new BufferedReader(new FileReader(fileName)); 
            String line = reader.readLine(); 
            while (line != null) { 
                String[] split = line.split("\|"); 
                Persona p = new PersonaJavaImpl(); 
                p.setNome(split[0]); 
                p.setCognome(split[1]); 
                p.setDataDiNascita(format.parse(split[2])); 
                Indirizzo indirizzo = new IndirizzoJavaImpl(); 
                indirizzo.setViaNumero(split[3]); 
                indirizzo.setCitta(split[4]); 
                p.setIndirizzo(indirizzo); 
                l.add(p); 
 
                line = reader.readLine(); 
            } 
        } catch (IOException e) { 
            throw new RuntimeException(e); 
        } catch (ParseException e) { 
            throw new RuntimeException(e); 
        } 
        finally { 
            if (reader != null) { 
                try { 
                    reader.close(); 
                } catch (IOException e) { 
                    throw new RuntimeException(e); 
                } 
            } 
        }(ad esempio il blocco finally) 
        return l; 
    } 
}

Le eccezioni sono state gestite per semplicità con eccezioni runtime, nonostante questo la gestione degli errori non è banale. Analizzando il metodo importa si nota che una parte è di gestione e lettura del file di testo e un'altra parte rappresenta la conversione in oggetto di una riga del file.
Quindi possiamo creare una classe che racchiude il codice riutilizzabile in altri contesti per gestire la lettura del file:

    public class ImportatoreTxtGenericoJavaImpl<T> implements Importatore<T> {  
 
    private final String fileName;  
    private final ImportatoreRiga<T> importatoreRiga;  
 
    public ImportatoreTxtGenericoJavaImpl(String fileName,   
            ImportatoreRiga<T> importatoreRiga) {  
        this.fileName = fileName;  
        this.importatoreRiga = importatoreRiga;  
    }  
 
    public List<T> importa() {  
        List<T> l = new ArrayList<T>();  
        BufferedReader reader = null;  
        try {  
            reader = new BufferedReader(new FileReader(fileName));  
            String line = reader.readLine();  
            while (line != null) {  
                l.add(importatoreRiga.importa(line));  
                line = reader.readLine();  
            }  
        } catch (IOException e) {  
            throw new RuntimeException(e);  
        }  
        finally {  
            if (reader != null) {  
                try {  
                    reader.close();  
                } catch (IOException e) {  
                    throw new RuntimeException(e);  
                }  
            }  
        }  
        return l;  
    }  
}  


una interfaccia usata da questa classe che si occupa di trasformare in oggetto una singola riga:
   
public interface ImportatoreRiga<T> {  
        T importa(String s);  
    } 


e una implementazione di questa interfaccia utilizzata nel nostro esempio che crea un oggetto Persona partendo da una riga:

public class ImportatorePersona implements ImportatoreRiga<Persona> {  
 
    private static SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");  
 
    public Persona importa(String s) {  
        try {  
            String[] split = s.split("\|");  
            Persona p = new PersonaJavaImpl();  
            p.setNome(split[0]);  
                 p.setCognome(split[1]);  
                 p.setDataDiNascita(format.parse(split[2]));  
                 Indirizzo indirizzo = new IndirizzoJavaImpl();  
                 indirizzo.setViaNumero(split[3]);  
                 indirizzo.setCitta(split[4]);  
                 p.setIndirizzo(indirizzo);  
                 return p;  
             } catch (ParseException e) {  
                 throw new RuntimeException(e);  
             }  
         }  
     }

Il codice interessante della classe ImportatorePersona è il metodo importa, capiamo il perché. ImportatoreTxtGenericoJavaImpl ha bisogno di sapere quale metodo invocare per trasformare una riga di un file di testo in un oggetto. Quindi l'interfaccia ImportatoreRiga esiste solo per wrappare il metodo da passare.

Questo comportamento in java è molto frequente (per esempio in tutti i listener), per poterlo implementare più velocemente sono state create le classi interne anonime. Per esempio: possiamo utilizzare la classe ImportatoreTxtGenericoJavaImpl senza definire esplicitamente una implementazione di ImportatoreRiga:

List<Persona> persone = new ImportatoreTxtGenericoJavaImpl<Persona>( 
    "resources/persone.txt",  
    new ImportatoreRiga<Persona>() { 
 
        public Persona importa(String s) { 
                Persona p = new PersonaJavaImpl(); 
                //.... 
                return p; 
        } 
    } 
    ).importa(); 


Usando le classi anonime si ottiene codice più compatto ma meno leggibile soprattutto per i non esperti.
La classe Groovy che implementa l'importazione è la seguente:
class ImportatoreTxtGroovyImpl implements Importatore<Persona> { 
     
    String nomeFile 
 
    private static SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy") 
     
    List<Persona> importa() { 
        List<Persona> l = new ArrayList<Persona>() 
        new File(nomeFile).eachLine { 
                String[] split = it.split("\|") 
                Persona p = new PersonaGroovyImpl() 
                p.nome = split[0] 
                p.cognome = split[1] 
                p.dataDiNascita = format.parse(split[2]) 
                Indirizzo indirizzo = new IndirizzoGroovyImpl() 
                indirizzo.viaNumero = split[3] 
                indirizzo.citta = split[4] 
                p.indirizzo = indirizzo 
                l.add(p) 
            } 
            return l 
        } 
    } 

La classe java ImportatoreTxtGenericoJavaImpl è in pratica già messa a disposizione da Groovy con il metodo eachLine di File, da notare che File è una classe java standard a cui sono stati aggiunti dei metodi di utilità utilizzabili da codice Groovy.
Ma cos'è quel blocco di codice scritto dopo il metodo eachLine?

E' una closure, il costrutto nativo Groovy per passare un blocco di codice come argomento di un metodo. In pratica una closure è un oggetto contenente un blocco di codice, nell'esempio appena visto viene passato al metodo eachLine (le parentesi tonde dopo il metodo nel caso delle closure sono opzionali). Quando una closure ha un solo parametro in ingresso la definizione di tale parametro può essere omessa, all'interno della closure è possibile accedere al parametro con la variabile it. I parametri di un metodo possono essere specificati in una lista seguita dai caratteri ->, per esempio:

    def map = [ 'a', 7, 'b', new Date(), new Date() + 1 ].groupBy{ it.class } 
     
    map.eachWithIndex { e, indice -> 
        println "${indice} Classe ${e.key} valori ${e.value}" 
    } 

 Questo codice sfrutta due closure (la seconda con una definizione esplicita dei parametri) e due metodi di List e Map che prendono una clusure in ingresso. Groovy definisce molti metodi utili sulle collection che sfruttano le closure, per esempio each, collect, find, findAll, min, max.
Il metodo groupBy itera sugli oggetti della lista e raggruppa gli elementi in base al valore di ritorno della closure passata, in un metodo o una closure groovy la keyword return può essere omessa in quanto viene ritornata la valutazione dell'ultimo comando.
l metodo eachWithIndex itera sugli elementi di una collection e passa alla closure l'i-esimo elemento e l'indice dell'elemento. L'istruzione all'interno della closure sfrutta le GString di groovy, usando la sintassi ${nomeVariabile} è possibile creare facilmente stringhe contenenti valori delle variabili disponibili.
Come ultimo esempio vediamo come implementare in Groovy la classe che esporta una lista di persone in un file xml. Per il corrispondente  esempio in java dovremmo utilizzare dom o librerie esterne tipo jdom, jaxb, castor o altre ancora.

L'implementazione groovy è la seguente:
class EsportatoreXmlGroovyImpl implements Esportatore {  
      
    private String nomeFile  
      
    EsportatoreXmlGroovyImpl(String nomeFile) {  
        this.nomeFile = nomeFile  
    }  
      
    private SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");  
          
        void esporta(List<Persona> persone) {  
            def writer = new FileWriter(nomeFile);  
            MarkupBuilder builder = new MarkupBuilder(writer)  
              
            try {  
                builder.listaPersone(totale: persone.size()) {  
                    persone.eachWithIndex { p, pos ->  
                        builder.persona(indice: pos) {  
                            nome(p.nome)  
                            cognome(p.cognome)  
                            dataDiNascita(formatter.format(  
                                p.dataDiNascita))  
                            indirizzo {  
                                viaNumero(p.indirizzo.viaNumero)  
                                citta(p.indirizzo.citta)  
                            }  
                        }  
                    }  
                }  
            }  
            finally {  
                if (writer != null) {  
                    writer.close();  
                }  
            }  
              
        }  
    }   

Questa classe fa uso dei builder Groovy, un concetto un po' particolare ma utilissimo. In pratica possono essere invocati sull'oggetto builder dei metodi con nome corrispondente al nome dell'elemento xml che si vuole creare.
Tali metodi non sono definiti normalmente, le chiamate vengono intercettate e viene creato il codice xml in automatico in base al nome del metodo e ai parametri passati. Se al metodo viene passata una coppia, viene aggiunto un attributo all'elemento xml, se viene passato un valore tale valore viene inserito all'interno dell'elemento.
Per creare elementi annidati basta passare una closure che crea gli elementi interni. Insomma è molto più difficile spiegare il funzionamento interno di un builder che non utilizzarlo :-)  Infatti c'è una relazione molto stretta fra il codice appena visto e l'xml che viene generato, l'indentazione aiuta molto in quanto il codice Groovy è indentato nello stesso modo del codice xml generato.

Concludiamo creando una semplice classe java che legge un file di testo e esporta il contenuto in xml:
    public class MainJava {  
      
        public static void main(String[] args) {  
            List<Persona> persone = new ImportatoreTxtJavaImpl("resources/persone.txt").importa();  
            new EsportatoreXmlGroovyImpl("resources/persone.xml").esporta(persone);  
        }  
      
    }  


Si può notare come nel metodo main siano presenti sia chiamate a metodi di classi java sia chiamate a metodi di classi groovy, addirittura la lista di persone importate da un metodo java (quindi oggetti java) viene passata a un metodo Groovy che la esporta in xml. Groovy può essere usato anche come un linguaggio di scripting, quindi è possibile definire codice all'esterno di una classe, per esempio:

    package groovytutorial.groovyimpl  
      
    def dir = "resources"  
    def persone = new ImportatoreTxtGroovyImpl(nomeFile: "${dir}/persone.txt").importa()  
    new EsportatoreXmlGroovyImpl("${dir}/persone.xml").esporta(persone)  

In questo codice sono presenti anche variabili non tipizzate per le quali è stata usata la keyword def (l'analogo di var in javascript). Su internet si trovano pareri discordanti riguardo all'uso di variabili non tipizzate in Groovy, molto dipende dall'esperienza personale, probabilmente uno sviluppatore java si troverà molto più a suo agio usando variabili tipizzate.

Il nostro esempio si è concluso, è il momento di qualche considerazione (ovviamente personale):

  • Groovy è l'ideale se avete bisogno di un linguaggio di scripting da usare all'interno di un progetto per modellare comportamenti dinamici definibili ad esempio dall'utente
  • la sintassi Groovy è uno dei suoi punti più potenti, molte cose che in java sono difficili o poco intuitive in Groovy si scrivono al volo
  • l'integrazione perfetta con codice java esistente permette l'introduzione graduale di Groovy, un buon inizio potrebbe essere utilizzare Groovy per scrivere i test junit di un applicativo java
  • il supporto per Groovy dei vari ide è ancora ben lontano da quello esistente per java. Tanto per fare un esempio: in Eclipse è assente il menù refactor e il menù source è quasi vuoto (in pratica non è presente molto di quello “che fa la differenza” in Eclipse)


Le mie uniche perplessità sono dovute al supporto ancora incompleto in Eclipse, per il resto è sicuramente un linguaggio da provare.

Riferimenti:


Sito ufficiale:  Groovy.codehaus.org

Libro: Groovy in action

Blog di Fabio Collini: blog2j

Username:
Password:
To sign up for an account, click register... Register
Hide





Powered By



Campagna Anti-IF


Skin


PARTNER
Zio Budda
HostingJava


LICENZA



Eccetto dove diversamente specificato, i contenuti di questo sito sono rilasciati sotto licenza Creative Commons

Sitemap  © 2002-2004 Copyright Information. Privacy . Today is domenica 1 agosto 2010