MokaByte 84 - Aprile 2004 
Groovy
di
Massimiliano Bigatti

L'idea è semplice quanto intrigante, sia per chi la sta sviluppando questo nuovo linguaggio, sia per lo sviluppatore che avrà presto a disposizione una possibile alternativa, più agile, per sviluppare sulla piattaforma Java.


Groovy propone molte caratteristiche interessanti, sviluppate attorno ad una sintassi più simile ai linguaggi di scripting che al rigoroso e preciso linguaggio Java. Gli statunitensi identificano questo come syntax sugar: sintassi non indispensabile, ma che consente di esprimere più logica con meno codice.
Groovy pu˜ essere utilizzato in alternativa al compilatore javac per produrre bytecode, oppure per implementare lo scripting in applicazioni Java.

 

Closures
Fin dai tempi del Clipper, ed anche più recentemente con linguaggi come Python e Ruby, esisteva il concetto del blocco di codice, che prevede la memorizzazione di una porzione di codice all'interno di una variabile per poterla manipolare.
Una closures di Groovy pu˜ essere immaginata come una classe interna anonima che hanno un solo metodo che, e questo è il concetto nuovo, è anonimo esso stesso. Il vantaggio delle closures è che non richiede la dichiarazione di classi e metodi; ad esempio, il seguente codice calcola la sommatoria di un insieme di valori (la parola chiave it è il valore corrente):

count = 0
[1, 2, 3, 4].each { count += it }

Un tipico utilizzo delle closures è la sostituzione degli iteratori, ma vengono utili anche con la gestione dei database.

SQL
Groovy appiattisce infatti l'accesso alle basi dati nascondendo le API JDBC dietro una sintassi che permette l'accesso ai record ed alle colonne con una sintassi simile a quella di Expression Language (tecnologia JSP). Nell'esempio seguente, viene eseguita una query e creata una closure con i dati risultanti; per accedere alla colonna name viene utilizzata la sintassi it.name:

import groovy.sql.Sql
import groovy.sql.TestHelper

foo = 'cheese'
sql = TestHelper.makeSql()

sql.eachRow("select * from FOOD where type=${foo}") {
  println "Gromit likes ${it.name}"
}

 

Markup
Un'altra caratteristica interessante è il supporto ai linguaggi di markup come XML, HTML, SAX, W3C DOM, Ant, ma anche interfacce Swing. La cosa interessante è che, indipendentemente dal tipo di dato che si sta costruendo, la sintassi è uguale, ad esempio, il seguente codice esegue una query sul database e costruisce un flusso XML con i dati ottenuti:

import groovy.sql.Sql
import groovy.sql.TestHelper
import groovy.xml.MarkupBuilder

sql = TestHelper.makeSql()
xml = new MarkupBuilder()

ignore = 'James'
sql.queryEach ("select * from person where firstname != $${ignore}") {
    person | xml.customer(
    id:person.id, type:'Customer', name:person.firstname + " " + person.lastname)
}

Il programma genera un output simile:

<customers>
<customer id="123" type="Customer" foo="whatever">
<role>partner</role>
<name>James</name>
<location id="5" name="London"/>
</customer>
</customers>

La tipologia di dato costruita è identificata dal builder (p.e. DOMBuilder, SAXBuilder, MarkupBuilder, AntBuilder, SwingBuilder); in funzione del tipo utilizzato si otterrà un risultato differente.

 

Collezioni
Un argomento caro ai linguaggi di scripting è per˜ il supporto alle collezioni, del tipo mappe o liste. Groovy consente di generare dinamicamente liste utilizzando le parentesi quadre:

list = [1, 2, 'hello', new java.util.Date()]
assert list.size() == 4
assert list.get(2) == 'hello'

Le mappe hanno un approccio simile, ma richiedono di specificare il valore della chiave da associare a ciascun valore, elemento che si indica utilizzando i due punti (:):

map = ['name':'James', 'location':'London']
assert map.size() == 2
assert map.get('name') == 'James'

L'iterazione all'interno di una lista è possibile attraverso la parola chiave for, che assomiglia molto alla nuova for di Java 1.5:

list = [1, 2, 3]
for (i in list) { println i }

 

Javabean più facili
Un'altra area dove Groovy semplifica il lavoro del programmatore è quella dei Javabean che possono essere specificati in modo pià rapido, in particolare omettendo i getter ed i setter che in molte implementazioni non fanno niente di più che memorizzare e restituire il valore delle proprietà. Per questo motivo il tipico oggetto Customer assume questa forma:

import java.util.Date

class Customer {
  // properties
  Integer id
  String name
  Date dob

  // sample code
  static void main(args) {
    customer = new Customer(id:1, name:"Gromit", dob:new Date())
    println("Hello ${customer.name}")
  }
}

Un'altro elemento salta subito all'occhio: non è necessario un costruttore che salvi ciascun valore passato come parametro, ma i valori delle proprietà possono essere passati con la sintassi utilizzata per le mappe. Groovy appiattisce dunque Javabean e strutture dati standard.
Questa sintassi non è vincolante, in quanto è possibile utilizzare quella classica di Java; nell'esempio seguente sono mostrate una proprietà in read only ed una con setter protetto. Queste caratteristiche si ottengono con i normali modificatori di visibilità di Java.

class Foo {
  // read only
  private String name
  public String getName() { return name }

  // read only property con setter protetto
  Integer amount
  protected void setAmount(Integer amount) { this.amount = amount }
}

Sono inoltre supportate proprietà con tipo dinamico, che quindi possono ospitare riferimenti a tipi di dati non noti in fase di sviluppo.

 

File ed I/O
Un altro punto di similitudine con altri linguaggi di scripting è l'accesso facilitato ai file di testo; ad esempio, per produrre in output un file si pu˜ scrivere:

import java.io.File
new File("foo.txt").eachLine { line | System.out.println(line) }

Se il codice eseguito da eachLine() solleva una eccezione, è responsabilità di Groovy rilasciare tutte le risorse allocate dall'apertura del file (come chiudere il file stesso).
Anche l'accesso ai processi è molto semplice e nasconde del tutto l'utilizzo delle pipe:

process = "ls -l".execute()
println "Found text ${process.text}"

Per realizzare queste funzionalità, Groovy implementa modifiche alla libreria di base di Java. Ad esempio, BufferedReader guadagna la nuova getText(), BufferedWriter ottiene writeLine(), InputStream acquista eachLine(), readLines(), withReader(), withStream(). E questi sono solo esempi.

 

Conclusioni
Altre caratteristiche includono il supporto alle espressioni regolari tipo Perl, un nuovo linguaggio per i percorsi (come XPath o Jexel), il supporto ad Ant ed un sistema di template. Ulteriori moduli, in fase di sviluppo, includono un ponte con SWT ed il supporto ai Web Services con XML-RPC.
Per gli amanti degli IDE, sono disponibili anche plug-in per Eclipse, IntelliJ e JEdit, anche se il supporto è ancora basilare.

 
MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it