Presentazione introduttiva su Groovy e Grails tratta dal materiale della serie di eventi Into The Groovy del JUG Milano tenuta allo Spring Meeting del 26/06/2010 a Cagliari organizzato da JUG Sardegna http://www.jugsardegna.org/vqwiki/jsp/Wiki?26giugno2010
1. Groovy & Grails
Marcello Teodori
marcello.teodori@jugmilano.it
Java User Group Milano
http://www.jugmilano.it
Spring Meeting Giugno 2010 - Cagliari
2. Di cosa parleremo?
● what is Groovy?
● in viaggio da Java a Groovy, levando un
pezzo alla volta
● cosa c'è di nuovo in Groovy
● Groovy e testing
● Groovy e Spring
● Grails
Spring Meeting Giugno 2010 2
3. Due parole sullo speaker
● Coordinatore del JUG Milano
● Moderatore SpringFramework-IT
● Moderatore Gruppo Italiano Utenti Groovy
● Socio e CTO in ExcogitaNet
● più twitterer: http://twitter.com/magomarcelo
che blogger: http://magomarcelo.blogspot.com
● Speaker ad All4Web, evento RIA cross-community
● Organizzatore di Into The Groovy, ciclo di eventi e
workshop del JUG Milano in collaborazione con
BYTE-CODE
Spring Meeting Giugno 2010 3
5. Cosa è Groovy?
● Groovy è un linguaggio dinamico per la Java
Virtual Machine (JVM)
● si ispira a Smalltalk, Python e Ruby
● ma soprattutto... si integra con il
linguaggio e la piattaforma Java a tutti i
livelli
Spring Meeting Giugno 2010 5
6. Un po' di storia di Groovy
● nasce nel 2003 come progetto personale di
James Strachan: “a groovier Java”
http://radio.weblogs.com/0112098/2003/08/29.html
● JSR-241, poi bug, rallentamenti e disinteresse
● Guillame LaForge riprende il progetto nel 2005
● sempre nel 2005 Graeme Rocher crea Grails
● nel 2007 LaForge e Rocher fondano G2One
● G2One viene acquisita da SpringSource a fine
2008
Spring Meeting Giugno 2010 6
7. Groovy Tools
Installato Groovy sul nostro sistema (via
uncompress più aggiunta directory “bin” al
PATH di sistema), unico prerequisito il JDK
1.4+, abbiamo a nostra disposizione:
● groovy – l'interprete Groovy
● groovyc – il compilatore Groovy/Java
● groovysh – la CUI shell interattiva
● groovyConsole – la GUI shell
● groovy-all-*.jar – runtime JAR
● java2groovy – il “facilitatore” ;)
Spring Meeting Giugno 2010 7
8. Groovy OOP
In Groovy tutto è un oggetto (a differenza di Java che ha i tipi primitivi)
In Java, prima delle funzionalità di boxing/unboxing si dovevano per forza usare i
wrapper type come ponte tra i tipi primitivi e gli oggetti.
Inoltre, nessun reference type può essere impiegato come operando per
operatori come +,*,-
Esiste quindi una precisa dicotomia in Java tra reference e tipi primitivi.
Questo porta alla scrittura di codice eccessivamente prolisso, nonostante le
migliorie apportate da Java 5 in poi.
Ad esempio:
for (int i=0; i < listOne.size(); i++)
int first = listOne.get(i);
int second = listTwo.get(i);
int sum = first + second;
results.add (new Integer(sum));
Spring Meeting Giugno 2010 8
9. Operator Overloading
In Groovy avremmo potuto scrivere molto + semplicemente
results.add (first.plus(second))
Sfruttando il fatto che in Groovy è stato aggiunto ad Integer il metodo plus.
In realtà possiamo fare molto di più, grazie alla possibilità di effettuare in Groovy
l'overloading degli operatori, che possono in tal modo operare direttamente su
reference ad oggetti e non solo su tipi primitivi.
results.add (first + second)
Si può usare questa sintassi proprio perché in Integer è stato effettuato
l'overloading di tutti gli operatori.
Immaginiamo adesso di voler introdurre un nuovo tipo (classe) che possa essere
usato come operando da + ed ==
Tutto quello che dobbiamo fare è definire due metodi, plus ed equals,
non occorre implementare nessuna interfaccia particolare.
Spring Meeting Giugno 2010 9
10. Operator Overloading
class Money {
private int amount
private String currency
boolean equals (Object other) {
if (null == other) return false
if (! (other instanceof Money)) return false
if (currency != other.currency) return false
if (amount != other.amount) return false
return true
}
Money plus (Money other) {
if (null == other) return null
if (other.currency != currency) {
throw new IllegalArgumentException(
"cannot add $other.currency to $currency")
}
return new Money(amount + other.amount, currency)
}
Spring Meeting Giugno 2010 10
11. Operator Overloading
Avendo ridefinito gli operatori in tal modo, adesso i reference a Money possono
essere usati direttamente come operandi.
def oneEuro = new Money(1, 'EUR')
assert oneEuro == new Money(1, 'EUR')
assert oneEuro + oneEuro == new Money(2, 'EUR')
È possibile fornire più di una versione dello stesso operatore
Ad esempio:
Money plus (Integer more) {
return new Money(amount + more, currency)
}
Adesso potremmo scrivere
new Money(1, 'EUR') + 5
L'operatore (metodo) corretto sarà individuato da Groovy a runtime
Spring Meeting Giugno 2010 11
12. Optional Typing
Differentemente che in Java, in Groovy non è obbligatorio specificare il tipo statico
di una qualsiasi variabile
In realtà, dietro le quinte, tutto diventa in Groovy un java.lang.Object
La keyword def è usata per indicare che non si vuole usare uno specifico tipo statico.
def a = 1
def b = 1.0f // implicit typing
int i = 2 // explicit typing
Integer i2 = 3
È importante capire che Groovy è type-safe, anche se si usa l'optional typing.
A differenza di linguaggi non tipizzati, Groovy non consente di trattare un'istanza di
un tipo come se fosse un'istanza di un differente tipo, senza una conversione
disponibile.
Ad esempio non sarebbe possibile assolutamente trattare la stringa “1” come se
fosse un numero all'interno di un'operazione aritmetica.
Spring Meeting Giugno 2010 12
13. Groovy OOP
La definizione di una classe in Groovy è molto simile a quanto si fa in Java, con
alcune lievi differenze
in un singolo file .groovy è anzitutto possibile definire quante public class si
desiderano, non soltanto una,
I modificatori di accesso (private, protected, public, static e final) hanno lo stesso
significato di Java
Una variabile d'istanza al livello di visibilità default è in realtà una property.
Definire il tipo di una qualsiasi variabile d'istanza è opzionale.
class SomeClass { //instance var
public fieldWithModifier
String typedField
def untypedField
protected field1, field2, field3
private assignedField = new Date()
static classField
def someMethod() { //local var
def localUntypedMethodVar = 1
int localTypedMethodVar = 1
def localVarWithoutAssignment, andAnotherOne
}
}
Spring Meeting Giugno 2010 13
14. Groovy OOP
L'accesso alle variabili d'istanza può avvenire attraverso la consueta notazione con il
punto:
class MyClass {
public myField = 0
}
def myInstance = new MyClass()
myInstance.myField = 1
assert myInstance.count == 1
Tuttavia è disponibile anche un modo più dinamico di accedere le variabili di
istanza, attraverso l'operatore subscript
def fieldName = 'myField'
myInstance [fieldName] = 2
assert myInstance['myField'] == 2
Spring Meeting Giugno 2010 14
15. Groovy OOP
Anche nella definizione dei metodi valgono ragionamenti analoghi a quanto visto per
le variabili d'istanza.
Il modificatore di accesso di default è public.
Il tipo di ritorno, incluso void, è opzionale. Tuttavia, se non si specifica né il tipo di
ritorno né un modificatore di accesso, è obbligatorio usare la keyword def (vale anche
per i field).
Di seguito un esempio:
class AClass {
static void main(args) {
def aClass = new AClass()
aClass.publicVoid()
assert 'A' == aClass.publicNoType()
assert 'B' == aClass.publicWithType()
}
void publicVoid() { }
def publicNoType() { return 'A' }
String publicWithType() { return 'B' }
}
Spring Meeting Giugno 2010 15
16. Groovy OOP
Per definire i parametri formali di un metodo, Groovy offre ben quattro approcci
differenti, evidenziati nell'esempio seguente.
class ManyMethodsClass {
def firstApproach ( a, b, c = 0 ) {
return a + b + c
}
def secondApproach ( List args ) {
return args.inject(0) { sum,i -> sum += i }
}
def thirdApproach ( a, b, Object[] optionals ) {
return a + b + secondApproach ( optionals.toList() )
}
def fourthApproach ( Map args ) {
[ 'a', 'b', 'c']. each { args.get(it,0) }
return args.a + args.b + args.c
}
}
Spring Meeting Giugno 2010 16
17. Groovy OOP
E di seguito un esempio di invocazione dei vari metodi
def instance = new ManyMethodsClass()
assert 2 == instance.firstApproach(1,1)
assert 3 == instance.firstApproach(1,1,1)
assert 2 == instance.secondApproach ( [1,1] )
assert 3 == instance.secondApproach ( [1,1,1] )
assert 2 == instance.thirthApproach ( 1,1 )
assert 3 == instance.thirthApproach ( 1,1,1 )
assert 2 == instance.fourthApproach( a:1, b:1)
assert 3 == instance.fourthApproach( a:1, b:1, c:1)
assert 1 == instance.fourthApproach( c:1 )
L'ultimo caso dettaglia l'utilizzo della tecnica di passaggio dei parametri per nome
anzichè posizionale.
Spring Meeting Giugno 2010 17
18. Groovy OOP
Come in Java, anche in Groovy le istanze vengono inizializzate a partire da un
costruttore. Nel realizzare i costruttori si può trarre beneficio dalle tecniche viste nel
passaggio dei parametri ai metodi.
La forma più familiare di costruttore è la seguente
class Aclass {
String first, second;
Aclass (first, second) {
this.first = first
this.second = second
}
}
Un oggetto di tale classe può essere istranziato in tre modi diversi:
1) def instance = new Aclass (“value1”, “value2”)
2) def instance = [ “value1”, “value2” ] as Aclass // coercion con as
3) Aclass instance = [ ”value1”, ”value2” ] // coercion in assegnamento
Spring Meeting Giugno 2010 18
19. Groovy OOP
Se non si vuole definire esplicitamente un costruttore, è possibile sfruttare
anche in fase di istanziazione la flessibilità dei named parameters.
class Aclass {
String first, second;
}
new Aclass()
new Aclass ( first: 'value' )
new Aclass ( second: 'value' )
new Aclass ( first: 'value', second: 'value' )
Spring Meeting Giugno 2010 19
20. POJO vs. POGO
Java Groovy
package it.jug...odel; package it.jug...odel;
public class Product { class Product {
private Integer id; Integer id
private String name; String name
public Long getId() { String toString() {
return id; name
} }
}
public void setId(Long id) {
this.id = id;
}
...
public String toString() {
return name;
}
}
Spring Meeting Giugno 2010 20
21. Groovy OOP - multimethod
In Java il processo di linking dei metodi ha regole ben precise:
La firma esatta del metodo da invocare viene effettuata sulla base del tipo dinamico del
reference e del tipo statico dei parametri.
public class Parent {
public void method (Object o) { System.out.println("Object in Parent"); }
public void method (String s) { System.out.println("String in Parent" ); }
}
public class Child {
public void method (Object o) { System.out.println("Object in Child"); }
public void method (String s) { System.out.println("String in Child" ); }
public static void main(String[] a){
Parent child = new Child();
child.method("ciao"); // stampa ”String in child”
Object oo = "bau";
child.method(oo); // stampa ”Object in child”
}
In Groovy, al contrario, nel decidere il metodo da invocare, viene usato il tipo dinamico
anche dei parametri. Il codice Groovy equivalente invocherebbe 2 volte il metodo della
sottoclasse che riceve stringhe.
Spring Meeting Giugno 2010 21
22. Collection - Range
Pensiamo a quante volte in Java ci si trova a scrivere codice come il seguente:
for (int i=0; i<upperBound; i++) {
// do something with i
}
oppure
if (x >= 0 && x <= upperBound) {
// do something with x
}
Non è difficilissimo da capire, ma potrebbe essere scritto in maniera più semplice
e soprattutto più espressiva
Per questo motivo è stato inserito nativamente in Groovy il concetto di Range:
un range ha un left bound ed un right bound.
È possibile specificare una qualsiasi azione per ogni elemento di un Range.
Spring Meeting Giugno 2010 22
23. Collection - Range
In definitiva: un Range è un intervallo associato ad una strategia per
interagire con i suoi elementi
Un Range può essere istanziato tramite la notazione .. o attraverso l'uso esplicito
di un costruttore
assert (0..10).contains(0)
assert (0..10).contains(10) // true, estremi inclusi
assert (0..10).contains(12) // false
def a = 0..10
assert a instanceof Range
assert a.contains(5)
a = new IntRange (0,10)
assert a.contains (5)
def today = new Date()
def yesterday = today-1
assert (yesterday..today).size() == 2
Spring Meeting Giugno 2010 23
24. Collection - Range
Un Range è un oggetto di classe groovy.lang.Range che espone una serie di
metodi di utilità.
Quelli di utillizo più comune sono
each: esegue una specifica closure per ogni elemento del range
contains: determina se un elemento fa parte o meno di un range
Ad esempio:
result = ''
(5..9).each {
element -> result += element
}
println result
assert result == '56789'
Spring Meeting Giugno 2010 24
25. Collection - List
Le liste in Groovy uniscono la versatilità e semplicità degli array alla dinamicità di
java.util.List.
Dichiarare una lista in Groovy è banale:
implicitList = [1,2,3]
assert myList.size() == 3
assert myList[0] == 1
assert myList instanceof ArrayList
Oppure
explicitList = new ArrayList()
explicitList.addAll(implicitList)
assert explicitList.size() == 3
explicitList[0] = 10
assert explicitList[0] == 10
explicitList = new LinkedList(implicitList)
assert explicitList.size() == 3
È anche possibile costruire una Lista a partire da un Range
longList = (0..1000).toList()
assert longList[555] == 555
Spring Meeting Giugno 2010 25
26. Collection - List
L'utilizzo combinato di liste ed overloading degli operatori rende la scrittura di
codice che gestisce collezioni indexed estremamente semplice e compatto.
myList = ['a','b','c','d','e','f']
assert myList[0..2] == ['a','b','c'] // getAt (range)
assert myList[0,2,4] == ['a','c','e'] // getAt (collection indexed)
myList[0..2] = ['x','y','z'] // putA
assert myList == ['x','y','z','d','e','f']
myList[3..5] = []
assert myList == ['x','y','z']
myList[1..1] = ['y','1','2']
assert myList == ['x','y','1','2','z']
Oltre all'operatore equals nel codice sopra è visibile l'uso anche dell'operatore subscript,
implementato tramite la coppia di metodi getAt e putAt
Spring Meeting Giugno 2010 26
27. Collection - List
Oltre agli operatori di subscripting, sono disponibili anche gli operatori
+ (plus), - (minus), * (multiply), << (leftshift)
Essi operano nel modo seguente:
MyList = [ ]
myList += 'a'
assert myList == ['a']
myList += ['b','c']
assert myList == ['a','b','c']
MyList = [ ]
myList << 'a' << 'b'
assert myList == ['a','b']
assert myList - ['b'] == ['a']
assert myList * 2 == ['a','b','a','b']
Spring Meeting Giugno 2010 27
28. Collection - List
L'interfaccia List in Groovy aggiunge un ricco insieme di metodi alle già ricche
funzionalità presenti in Java.
assert [1,[2,3]].flatten() == [1,2,3]
assert [1,2,3].intersect([4,3,1])== [3,1]
list = [1,2,3]
popped = list.pop()
assert popped == 3
assert list == [1,2]
assert [1,2].reverse() == [2,1]
assert [3,1,2].sort() == [1,2,3]
assert list == [ [1,0], [0,1,2] ]
list = ['a','b','c']
list.remove(2)
assert list == ['a','b']
list.remove('b')
assert list == ['a']
Spring Meeting Giugno 2010 28
29. Collection - Map
La creazione di oggetti con interfaccia Map in Groovy gode di analoga
semplificazione: la Map per Groovy è una List con indici non Integer.
def map = [a: 1, b: 2, b: 3]
assert map.a == 1
assert map['a'] == 1
assert map.get('d', 'pippo') == 'pippo'
map.d = 'pippo'
assert map['d'] == 'pippo'
for (entry in map) {
println “$entry.key = $entry.value”
}
map.each { entry →
println “$entry.key = $entry.value”
}
map.each { key, value →
println “$key = $value”
}
Spring Meeting Giugno 2010 29
30. Closure
Le closure sono uno degli elementi che aumentano maggiormente l'espressività di
Groovy.
Mentre in un linguaggio tradizionale nelle variabili possono essere salvati
esclusivamente dati, un linguaggio che supporta le closure consente di salvare in
una variabile dati e codice.
Questo consente di creare algoritmi complessi con una sintassi molto semplice
Semplificando molto, una closure può essere pensata come uno speciale costrutto
per passare un blocco di codice come argomento di un metodo.
In pratica una closure è un oggetto contenente un blocco di codice.
Ad esempio, la seguente è la più semplice (ed in realtà inutile) forma di closure
def closure = { println "sono una closure!" }
closure() // stampa "sono una closure!"
Ad una closure è possibile passare dei parametri attraverso l'operatore - >
def closure = {x,y,z - > print x + y + z}
closure (1,2,3) //stampa 6
Spring Meeting Giugno 2010 30
31. Closure
Se la closure riceve un unico parametro, è possibile ometterlo nella definizione e
riferirlo con la variabile speciale it.
def clos = { print it }
clos( "parametro implicito" )
Le due forme seguenti di esprimere una closure sono perfettamente equivalenti:
def square = { operand - > operand * operand}
def square = { it * it }
L'aspetto più interessante delle closure è che possono essere passate ad un
qualsiasi metodo che la definisca tra i propri parametri formali.
Ad esempio, il metodo collect di Collection in Groovy ha la seguente definizione
List collect(Closure closure)
ed è pensato per iterare tutti gli elementi della Collection, applicando la funzione
passata nella closure e restituendo una nuova lista costruita con tali elementi.
Spring Meeting Giugno 2010 31
32. Closure – collection projection
Se volessimo una lista contenente il doppio di ciascun elemento presente in una lista
di partenza, sarebbe sufficiente scrivere
def list = [2,3,4]
def newList = [ ]
newList = list.collect { it*2 }
Questo consente di scrivere codice estremamente compatto ed espressivo
Ad esempio, data la definizione
class Employee {
def salary
}
Grazie alle closure, possiamo costruire una Lista di Employee a partire da un Range
di numeri in un'unica linea di codice, senza nemmeno un'iterazione
def emps = [10, 20, 30]. collect { val -> new Employee(salary:val) }
Spring Meeting Giugno 2010 32
33. Closure – collection filtering
E se volessivo una List di tutti gli employee che guadagnano tra 10000 e 20000?
Potremmo sfruttare il metodo definito nelle Collection di groovy
Collection findAll(Closure closure)
che restituisce una collection con solo gli elementi che soddisfano la condizione
specificata nella closure.
Nel nostro caso avremmo:
def interestingEmployees(emps) {
def min = 10000
def max = 20000
return emps.findAll { e -> e.salary < max && e.salary > min }
}
def emps = [10, 20, 30]. collect { val -> new Employee(salary:val) }
Collection c = interestingEmployees ( emps )
Spring Meeting Giugno 2010 33
34. Reflection in Groovy
In Java sin dalle prime versioni sono disponibili tecniche molto avanzate di
programmazione dinamica, attraverso l'uso delle Reflection API.
In Groovy questo concetto è ulteriormente potenziato e, soprattutto, reso molto più
semplice da utilizzare.
Ad esempio, è possibile invocare un metodo su un'istanza di classe SENZA
specificare il nome del metodo a tempo di compilazione:
class Cane extends Animale {
void corri () { println("sto correndo") }
void mangia (cibo) { println "sto mangiando " + cibo }
}
def someMethodInSomeClass (animale, nomeMetodo, Object[] params) {
animale."$nomeMetodo"(*params)
}
someMethodInSomeClass (new Cane(), "corri")
someMethodInSomeClass (new Cane(), "mangia" , "erba")
Un codice equivalente in Java sarebbe molto più lungo e complesso.
Spring Meeting Giugno 2010 34
35. ed ora uso Groovy al posto di Java!
paura eh?
Spring Meeting Giugno 2010 35
36. Groovy senza paura!
● scripting e codice di supporto
● unit testing
– GroovyTestCase per JUnit 3
– JUnit 4 annotation su classi Groovy
● mocking tramite supporto dynamic
languages di Spring con <lang:groovy../>
● progettazione domain model
● prototipazione rapida
Spring Meeting Giugno 2010 36
37. Grails come Groovy on Rails!
RAD per applicazioni web domain-driven
basato su convention over configuration
● Spring MVC in disguise
● DSL
– BeanBuilder per Spring
– GORM per Hibernate
● shell per automatizzare operazioni
● plugin system
– directory con ampia scelta
– community molto attiva
Spring Meeting Giugno 2010 37
38. Demo CRUD webapp Team/Players
tratto da presentazione di Davide Rossi al JUG Milano, 22/01/2009
● Create a new application
● Create domain classes
– Datasource
– Constraints
– Associations
● Create controllers / views
● Run the application
● Searching
– Dynamic finders
– HQL (Hibernate query language)
– Hibernate Criteria
● Pagination
Spring Meeting Giugno 2010 38
39. Conclusioni
● Java è così vecchio? Mettiamo Groovy al posto di Java
come linguaggio principale della JVM!
● Groovy e tecnologie correlate hanno un approccio in
stile “standing on the shoulders of giants”, cioè che
costruisce “sulle spalle dei giganti”: Java, Spring,
Hibernate, ecc.
● groovySpeed = JavaSpeed / 15
ma la velocità è così importante?
● invokedynamic in Java 7
● attenzione ad un uso “consapevole” di Groovy!
● codice di prototipo, produzione, supporto o testing...
c'è sempre un posto in cui Groovy può essere utile!
Spring Meeting Giugno 2010 39
40. Riferimenti
● Groovy
http://groovy.codehaus.org/
● Grails
http://www.grails.org/
● JUG Milano – Into The Groovy
http://www.jugmilano.it/vqwiki/jsp/Wiki?IntoTheGroovy
Spring Meeting Giugno 2010 40