Fort de ses 1.7 millions de téléchargements l'an passé, Groovy continue son bonhomme de chemin en tête parmi les langages de programmation alternatifs pour la JVM.
Groovy 2.0, sorti l'an passé, introduisait dans son offre de la modularité, le support de JDK 7 au niveau syntaxique avec "Project Coin" autant qu'au niveau JVM avec l'utilisation d'"invoke dynamic", et proposait des fonctionnalités de typage et de compilation statique.
Groovy 2.1, quant à lui, s'appuie sur ces bases pour compléter le support d'"invoke dynamic" pour plus de performances. Il propose des améliorations permettant de documenter, d'aider les IDEs, et de vérifier statiquement les Domain-Specific Languages construits avec Groovy. Vous pourrez créer des méta-annotations regroupant d'autres annotations, pour éviter l'annotation "hell". Et enfin, vous irez encore plus loin dans la customisation du compilateur !
Accrochez votre ceinture, paré au décollage !
19. Modularité
« Tout le monde n’a pas besoin de tout,
tout le temps, en même temps ! »
20. LamodularitédeGroovy
• Le JAR « groovy-all » de... 6 Mo !
• En plus du langage, des APIs :
• moteur de template, scripting the tâches Ant,
construction d’interfaces Swing...
• Proposer un coeur plus léger
• et des modules par API
• Brancher des méthodes d’extension
21. LesnouveauxJARs
• Un JAR principal plus petit 3 Mo
• Modules
– console
– docgenerator
– groovydoc
– groovysh
– ant
– bsf
– jsr-223
– jmx
– sql
– swing
– servlet
– templates
– test
– testng
– json
– xml
22. LesnouveauxJARs
• Un JAR principal plus petit 3 Mo
• Modules
– console
– docgenerator
– groovydoc
– groovysh
– ant
– bsf
– jsr-223
– jmx
– sql
– swing
– servlet
– templates
– test
– testng
– json
– xml
25. Lesmodulesd’extension
• Créer sa propre extension
• contribuer des méthodes d’instance
package
foo
class
StringExtension
{
static
introduces(String
self,
String
name)
{
"Hi
${name),
I’m
${self}"
}
}
//
usage:
"Guillaume".introduces("Cédric")
Même structure
que les catégories
26. Lesmodulesd’extension
• Créer sa propre extension
• contribuer des méthodes statiques
package
foo
class
StaticStringExtension
{
static
hi(String
self)
{
"Hi!"
}
}
//
usage:
String.hi()
27. Descripteurdesmodulesd’extension
• META-INF/
• services/
• org.codehaus.groovy.runtime.ExtensionModule
moduleName
=
stringExtensions
moduleVersion
=
1.0
//
liste
de
noms
de
classe
séparées
par
des
virgules
extensionClasses
=
foo.StringExtension
//
liste
de
noms
de
classe
séparées
par
des
virgules
staticExtensionClasses
=
foo.StaticStringExtension
30. Littérauxbinaires
• En plus de décimal, octal, et héxa...
• On a une représentation binaire :
int
x
=
0b10101111
assert
x
==
175
byte
aByte
=
0b00100001
assert
aByte
==
33
int
anInt
=
0b1010000101000101
assert
anInt
==
41285
31. Les«underscores»dansleslittéraux
• Utilisation des « underscores » pour séparer
les unités au choix
long
creditCardNumber
=
1234_5678_9012_3456L
long
socialSecurityNumbers
=
999_99_9999L
float
monetaryAmount
=
12_345_132.12
long
hexBytes
=
0xFF_EC_DE_5E
long
hexWords
=
0xFFEC_DE5E
long
maxLong
=
0x7fff_ffff_ffff_ffffL
long
alsoMaxLong
=
9_223_372_036_854_775_807L
long
bytes
=
0b11010010_01101001_10010100_10010010
32. Catchd’exceptionsmultiples
• Un seul bloc catch pour plusieurs
exceptions, plutôt que dupliquer les blocs
try
{
/*
...
*/
}
catch(IOException
|
NullPointerException
e)
{
/*
un
seul
bloc
*/
}
35. Supportd’invokedynamicdeJDK7
• Nouveau « flag » pour compiler avec « indy »
• on proposera peut-être un backport (pour JDK < 7)
• Avantages
• plus de performance à l’exécution
• en théorie...
• Sur le long terme, on pourra remplacer
• « call site caching » ➔ MethodHandles
• « metaclass registry » ➔ ClassValues
• et le JIT « inlinera » plus facilement le code
36. Thème « statique »
Vérification statique de type
Compilation statique
38. Tout le monde n’a pas
besoin de dynamique
tout le temps !
39. Tout le monde n’a pas
besoin de dynamique
tout le temps !
Nah !
40. Vérificationstatiquedetype
• Le compilateur grincheux souhaite...
• dire quand il y a une faute de frappe
dans le nom d’une méthode ou d’une variable
• râler quand on appelle une méthode
inexistante
• ou quand on fait de mauvaises
affectations ou utilise un
mauvais type de retour
41. Vérificationstatiquedetype
• Le compilateur doit inférer les types...
• moins besoin de types explicites et de casts
• inférence fine
• « flow typing »
• « lowest upper bound »
42. Vérificationstatiquedetype
• Mais le compilateur doit comprendre les
méthodes d’extension de Groovy
• permet d’avoir un bon niveau de dynamisme
malgré les restrictions supplémentaires
45. Fautesdefrappe
import
groovy.transform.TypeChecked
void
method()
{}
@TypeChecked
test()
{
//
Cannot
find
matching
method
metthhoood()
metthhoood()
def
name
=
"Guillaume"
//
variable
naamme
is
undeclared
println
naamme
}
Erreur de
compilation
Erreur de
compilation
46. Fautesdefrappe
import
groovy.transform.TypeChecked
void
method()
{}
@TypeChecked
test()
{
//
Cannot
find
matching
method
metthhoood()
metthhoood()
def
name
=
"Guillaume"
//
variable
naamme
is
undeclared
println
naamme
}
Erreur de
compilation
Erreur de
compilation
Annotation niveau
classe ou méthode
47. Mauvaisesaffectationsdevariable
//
cannot
assign
value
of
type...
to
variable...
int
x
=
new
Object()
Set
set
=
new
Object()
String[]
strings
=
['a','b','c']
int
str
=
strings[0]
//
cannot
find
matching
method
plus()
int
i
=
0
i
+=
'1'
48. Mauvaisesaffectationsdevariable
//
cannot
assign
value
of
type...
to
variable...
int
x
=
new
Object()
Set
set
=
new
Object()
String[]
strings
=
['a','b','c']
int
str
=
strings[0]
//
cannot
find
matching
method
plus()
int
i
=
0
i
+=
'1'
Erreurs de
compilation
49. Mauvaisesaffectationsdevariable
//
cannot
assign
value
of
type...
to
variable...
int
x
=
new
Object()
Set
set
=
new
Object()
String[]
strings
=
['a','b','c']
int
str
=
strings[0]
//
cannot
find
matching
method
plus()
int
i
=
0
i
+=
'1'
Erreurs de
compilation
Erreurs de
compilation
50. Mauvaisesaffectationsdevariable
//
cannot
assign
value
of
type...
to
variable...
int
x
=
new
Object()
Set
set
=
new
Object()
String[]
strings
=
['a','b','c']
int
str
=
strings[0]
//
cannot
find
matching
method
plus()
int
i
=
0
i
+=
'1'
Erreurs de
compilation
Erreurs de
compilation
Erreurs de
compilation
51. Mauvaistypederetour
//
checks
if/else
branch
return
values
@TypeChecked
int
method()
{
if
(true)
{
'String'
}
else
{
42
}
}
//
works
for
switch/case
&
try/catch/finally
//
transparent
toString()
implied
@TypeChecked
String
greeting(String
name)
{
def
sb
=
new
StringBuilder()
sb
<<
"Hi
"
<<
name
}
52. Mauvaistypederetour
//
checks
if/else
branch
return
values
@TypeChecked
int
method()
{
if
(true)
{
'String'
}
else
{
42
}
}
//
works
for
switch/case
&
try/catch/finally
//
transparent
toString()
implied
@TypeChecked
String
greeting(String
name)
{
def
sb
=
new
StringBuilder()
sb
<<
"Hi
"
<<
name
}
Erreur de
compilation
53. Mauvaistypederetour
//
checks
if/else
branch
return
values
@TypeChecked
int
method()
{
if
(true)
{
'String'
}
else
{
42
}
}
//
works
for
switch/case
&
try/catch/finally
//
transparent
toString()
implied
@TypeChecked
String
greeting(String
name)
{
def
sb
=
new
StringBuilder()
sb
<<
"Hi
"
<<
name
}
Erreur de
compilation
Au final, appèle le
toString() de
StringBuilder
54. Inférencedetype
@TypeChecked
test()
{
def
name
=
"
Guillaume
"
//
String
type
infered
(even
inside
GString)
println
"NAME
=
${name.toUpperCase()}"
//
Groovy
GDK
method
support
//
(GDK
operator
overloading
too)
println
name.trim()
int[]
numbers
=
[1,
2,
3]
//
Element
n
is
an
int
for
(int
n
in
numbers)
{
println
n
}
}
55. Inférencedetype
@TypeChecked
test()
{
def
name
=
"
Guillaume
"
//
String
type
infered
(even
inside
GString)
println
"NAME
=
${name.toUpperCase()}"
//
Groovy
GDK
method
support
//
(GDK
operator
overloading
too)
println
name.trim()
int[]
numbers
=
[1,
2,
3]
//
Element
n
is
an
int
for
(int
n
in
numbers)
{
println
n
}
}
Variable
optionnellement typée
56. Inférencedetype
@TypeChecked
test()
{
def
name
=
"
Guillaume
"
//
String
type
infered
(even
inside
GString)
println
"NAME
=
${name.toUpperCase()}"
//
Groovy
GDK
method
support
//
(GDK
operator
overloading
too)
println
name.trim()
int[]
numbers
=
[1,
2,
3]
//
Element
n
is
an
int
for
(int
n
in
numbers)
{
println
n
}
}
Variable
optionnellement typée
Type String inféré
57. Inférencedetype
@TypeChecked
test()
{
def
name
=
"
Guillaume
"
//
String
type
infered
(even
inside
GString)
println
"NAME
=
${name.toUpperCase()}"
//
Groovy
GDK
method
support
//
(GDK
operator
overloading
too)
println
name.trim()
int[]
numbers
=
[1,
2,
3]
//
Element
n
is
an
int
for
(int
n
in
numbers)
{
println
n
}
}
Variable
optionnellement typée
Méthode trim() ajoutée
dynamiquement par Groovy
Type String inféré
58. Inférencedetype
@TypeChecked
test()
{
def
name
=
"
Guillaume
"
//
String
type
infered
(even
inside
GString)
println
"NAME
=
${name.toUpperCase()}"
//
Groovy
GDK
method
support
//
(GDK
operator
overloading
too)
println
name.trim()
int[]
numbers
=
[1,
2,
3]
//
Element
n
is
an
int
for
(int
n
in
numbers)
{
println
n
}
}
Variable
optionnellement typée
Type des éléments
d’un tableau inféré
Méthode trim() ajoutée
dynamiquement par Groovy
Type String inféré
59. Mélangerdynamiqueetstatiquementvérifié
@TypeChecked
String
greeting(String
name)
{
//
call
method
with
dynamic
behavior
//
but
with
proper
signature
generateMarkup(name.toUpperCase())
}
//
usual
dynamic
behavior
String
generateMarkup(String
name)
{
def
sw
=
new
StringWriter()
new
MarkupBuilder(sw).html
{
body
{
div
name
}
}
sw.toString()
}
60. Mélangerdynamiqueetstatiquementvérifié
@TypeChecked
String
greeting(String
name)
{
//
call
method
with
dynamic
behavior
//
but
with
proper
signature
generateMarkup(name.toUpperCase())
}
//
usual
dynamic
behavior
String
generateMarkup(String
name)
{
def
sw
=
new
StringWriter()
new
MarkupBuilder(sw).html
{
body
{
div
name
}
}
sw.toString()
}
Statiquement vérifié
61. Mélangerdynamiqueetstatiquementvérifié
@TypeChecked
String
greeting(String
name)
{
//
call
method
with
dynamic
behavior
//
but
with
proper
signature
generateMarkup(name.toUpperCase())
}
//
usual
dynamic
behavior
String
generateMarkup(String
name)
{
def
sw
=
new
StringWriter()
new
MarkupBuilder(sw).html
{
body
{
div
name
}
}
sw.toString()
}
Statiquement vérifié
Dynamique
64. Vérificationsparinstanceof
@TypeChecked
void
test(Object
val)
{
if
(val
instanceof
String)
{
println
val.toUpperCase()
}
else
if
(val
instanceof
Number)
{
println
"X"
*
val.intValue()
}
}
Pas besoin
de cast
Pas besoin
de cast
65. Vérificationsparinstanceof
@TypeChecked
void
test(Object
val)
{
if
(val
instanceof
String)
{
println
val.toUpperCase()
}
else
if
(val
instanceof
Number)
{
println
"X"
*
val.intValue()
}
}
Pas besoin
de cast
Pas besoin
de cast
Comprends la méthode du
GDK : String#multuply(int)
66. LowestUpperBound
• Le plus petit « super » type commun
• peut-être virtuel (« non-dénotable »)
@TypeChecked
test()
{
//
an
integer
and
a
BigDecimal
return
[1234,
3.14]
}
67. LowestUpperBound
• Le plus petit « super » type commun
• peut-être virtuel (« non-dénotable »)
@TypeChecked
test()
{
//
an
integer
and
a
BigDecimal
return
[1234,
3.14]
}
Type inféré :
List<Number & Comparable>
68. Flowtyping
• La vérification statique « suit » le type des
valeurs assignées dans les variables
@TypeChecked
test()
{
def
var
=
123
//
int
inferé
int
x
=
var
//
var
est
un
int
var
=
"123"
//
assigne
une
String
dans
var
x
=
var.toInteger()
//
pas
besoin
de
cast
var
=
123
x
=
var.toUpperCase()
//
erreur,
var
est
un
int
!
}
70. Pô très clean ton code,
non mais allô quoi ?
Ben non !
71. Vérificationstatiqueetcodedynamique
• La vérification statique à la compilation
• @TypeChecked ne change pas le comportement
• ne pas confondre avec compilation statique
• La plupart des fonctionnalités dynamiques
ne peuvent être vérifiées
• changement de métaclasse, catégories...
• variables dynamiques dans le « script binding »
• Mais métaprogrammation compile-time OK
• si suffisamment d’informations de type
79. Mais si c’est pô dynamique,
on peut compiler
statiquement ?
80. Mais si c’est pô dynamique,
on peut compiler
statiquement ?
Ben oui !
81. Compilationstatique
• Etant donné que le code est vérifié, que l’on
infère beaucoup d’information de type...
on peut aussi bien compiler statiquement !
• càd générer le même bytecode que javac
• Aussi intéressant pour ceux qui sont bloqués
en JDK < 7, pour bénéficier d’améliorations
de performances
82. Avantagesdelacompilationstatique
• On gagne :
• de la « type safety »
• grâce à la vérification statique
• car la compilation statique s’appuie dessus
• du code plus rapide
• aussi proche que la performance de Java
• du code immunisé contre le « monkey patching »
• la métaprogrammation dynamique peut
interférer avec vos frameworks
• du bytecode généré plus petit
85. Inconvénientsdelacompilationstatique
• On y perds...
• Certaines fonctionnalités dynamiques
• changement de métaclasse, catégories
• Le « dynamic dispatch » de méthode peut différer
• même si grâce à l’inférence de type, elle est aussi
proche de Groovy « classique » que possible
86. Mixercompilationstatiqueetcodedynamique
@CompileStatic
String
greeting(String
name)
{
//
call
method
with
dynamic
behavior
//
but
with
proper
signature
generateMarkup(name.toUpperCase())
}
//
usual
dynamic
behavior
String
generateMarkup(String
name)
{
def
sw
=
new
StringWriter()
new
MarkupBuilder(sw).html
{
body
{
div
name
}
}
sw.toString()
}
87. Mixercompilationstatiqueetcodedynamique
@CompileStatic
String
greeting(String
name)
{
//
call
method
with
dynamic
behavior
//
but
with
proper
signature
generateMarkup(name.toUpperCase())
}
//
usual
dynamic
behavior
String
generateMarkup(String
name)
{
def
sw
=
new
StringWriter()
new
MarkupBuilder(sw).html
{
body
{
div
name
}
}
sw.toString()
}
Statiquement compilé
88. Mixercompilationstatiqueetcodedynamique
@CompileStatic
String
greeting(String
name)
{
//
call
method
with
dynamic
behavior
//
but
with
proper
signature
generateMarkup(name.toUpperCase())
}
//
usual
dynamic
behavior
String
generateMarkup(String
name)
{
def
sw
=
new
StringWriter()
new
MarkupBuilder(sw).html
{
body
{
div
name
}
}
sw.toString()
}
Statiquement compilé
Dynamique
89. Mixercompilationstatiqueetcodedynamique
@CompileStatic
String
greeting(String
name)
{
//
call
method
with
dynamic
behavior
//
but
with
proper
signature
generateMarkup(name.toUpperCase())
}
//
usual
dynamic
behavior
String
generateMarkup(String
name)
{
def
sw
=
new
StringWriter()
new
MarkupBuilder(sw).html
{
body
{
div
name
}
}
sw.toString()
}
Statiquement compilé
Dynamique
Appel d’une
méthode au
contenu
dynamique
90. Mixercompilationstatiqueetcodedynamique
@CompileStatic
String
greeting(String
name)
{
//
call
method
with
dynamic
behavior
//
but
with
proper
signature
generateMarkup(name.toUpperCase())
}
//
usual
dynamic
behavior
String
generateMarkup(String
name)
{
def
sw
=
new
StringWriter()
new
MarkupBuilder(sw).html
{
body
{
div
name
}
}
sw.toString()
}
Statiquement compilé
Dynamique
Appel d’une
méthode au
contenu
dynamique
La signature d’une
méthode est un
contrat !
91. Etlaperformancedanstoutça?
• Comparaisons entre :
• Java
• Groovy
• avec compilation statique — Groovy 2.0
• avec optimisations types primitifs — Groovy 1.8+
• sans optimisation — Groovy 1.7
95. Supportcompletd’invokedynamic
• Dans Groovy 2.0, tous les appels de méthode
ne passaient pas par « indy »
• seulement les appels de méthodes normals
• utilisation conjointe du « call site caching »
• Sur JDK 7, avec le JAR « indy », Groovy 2.1
utilise « invoke dynamic » partout
• Sur JDK < 7, encore du « call site caching »
97. Méta-annotations
• Créer des méta-annotations
qui combinent et / ou paramétrisent
d’autres annotations
• Et qui fonctionnent avec les annotations des
transformations d’AST
105. Annotation@DelegatesTo
• La vérification statique fonctionne bien avec
certains Domain-Specific Languages
• « command chains », méthodes d’extension...
• Mais pas pour les DSLs utilisant des
closures et de la délégation d’appel
• souvent utilisé dans les DSLs comme Gradle
task
copyTask(type:
Copy)
{
from
'src/main/webapp'
into
'build/explodedWar'
}
109. Annotation@DelegatesTo
class
ExecSpec
{
void
foo()
}
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c()
}
exec(spec)
{
foo()
}
Le vérificateur statique ne sait
rien de la méthode foo()
110. Annotation@DelegatesTo
class
ExecSpec
{
void
foo()
}
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c()
}
exec(spec)
{
foo()
}
Annoter avec
@DelegatesTo(ExecSpec)
Le vérificateur statique ne sait
rien de la méthode foo()
111. Annotation@DelegatesTo
• Avec une autre stratégie de délégation
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c.resolveStrategy
=
DELEGATE_FIRST
c()
}
112. Annotation@DelegatesTo
• Avec une autre stratégie de délégation
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c.resolveStrategy
=
DELEGATE_FIRST
c()
}
Annoter avec
@DelegatesTo(value = ExecSpec,
strategy = DELEGATE_FIRST)
115. Annotation@DelegatesTo
• Utiliser Target pour préciser à qui déléguer
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c()
}
@DelegatesTo.Target(‘‘id’’) @DelegatesTo(target = ‘‘id’’)
116. Annotation@DelegatesTo
• Intéressant surtout pour les DSLs utilisation
la délégation d’appel
dans les closures
• Excellent pour...
• documenter les APIs
• l’intégration avec l’IDE
• complétion de code, navigation...
• fonctionne avec la vérification statique et la
compilation statique
118. Etendrelevérificateurstatiquedetype
• Etendre le vérificateur pour
le rendre encore plus intelligent !
• voire même plus intelligent que celui de Java :-)
• En créant sa propre extension
@TypeChecked(extensions
=
'MyExtension.groovy')
void
exec()
{
//
code
to
be
further
checked...
}
119. Etendrelevérificateurstatiquedetype
• Etendre le vérificateur pour
le rendre encore plus intelligent !
• voire même plus intelligent que celui de Java :-)
• En créant sa propre extension
@TypeChecked(extensions
=
'MyExtension.groovy')
void
exec()
{
//
code
to
be
further
checked...
}
On pourra créer une
méta-annotation
120. • Aider le vérificateur lorsque...
• impossible d’inférer un type
• aucune méthode trouvée
• pas d’attribut trouvé
• assignation incorrecte
Etendrelevérificateurstatiquedetype
121. • Votre extension a accès à
une API orientée événement
Etendrelevérificateurstatiquedetype
• onMethodSelection
• afterMethodCall
• beforeMethodCall
• afterVisitMethod
• beforeVisitMethod
• methodNotFound
• unresolvedVariable
• unresolvedProperty
• unresolvedAttribute
• incompatibleAssignment
126. • Quelques exemples...
• Vérifier qu’une chaîne représentant
une requête SQL est valide
• Vérifier le type des arguments passés à
sprintf() avec le pattern de la chaîne
Etendrelevérificateurstatiquedetype
128. Customiserlecompilateur
• Groovy 1.8 a introduit la notion
de « customizer »
• rajouter des imports transparents
• appliquer des transformations d’AST
• filtrer / sécuriser les scripts
• Avec le « static type checker » et la «
compilation statique », on nous a demandé
s’il était possible de les appliquer par
défaut
129. Customiserlecompilateur
• Nouvelles options
• --basescript
pour définir une classe de base pour les scripts
• --configscript
pour indiquer un script qui va configurer
CompilerConfiguration
130. Customiserlecompilateur
• Rajouter la transformation @ToString
•
import
groovy.transform.ToString
import
org.codehaus.groovy.control.customizers
.ASTTransformationCustomizer
configuration.addCompilationCustomizer(
new
ASTTransformationCustomizer(ToString)
)
131. Customiserlecompilateur
• Rajouter la transformation @ToString
•
import
groovy.transform.ToString
import
org.codehaus.groovy.control.customizers
.ASTTransformationCustomizer
configuration.addCompilationCustomizer(
new
ASTTransformationCustomizer(ToString)
)
Instance de CompilerConfiguration
injectée par défaut
132. Customiserlecompilateur
• Un petit DSL pour simplifier la configuration
configuration.customizers
{
//
apply
to
MyBean.groovy
source(basename:
'MyBean')
{
ast(ToString)
}
}
133. Customiserlecompilateur
• Un petit DSL pour simplifier la configuration
configuration.customizers
{
//
apply
to
MyBean.groovy
source(basename:
'MyBean')
{
ast(ToString)
}
}
configuration.customizers
{
//
apply
to
*.gbean
files
source(extension:
'.gbean')
{
ast(ToString)
}
}
134. Customiserlecompilateur
• Un petit DSL pour simplifier la configuration
configuration.customizers
{
//
apply
to
MyBean.groovy
source(basename:
'MyBean')
{
ast(ToString)
}
}
configuration.customizers
{
//
apply
to
*.gbean
files
source(extension:
'.gbean')
{
ast(ToString)
}
}
configuration.customizers
{
//
custom
filter
logic
source(unitValidator:
{
unit
-‐>
...
})
{
ast(ToString)
imports
{
staticStar
'java.lang.Math'
}
}
}
135. Pour en savoir plus...
Groovy2.0
http://groovy.codehaus.org/Groovy+2.0+release+notes
Groovy2.1
http://groovy.codehaus.org/Groovy+2.1+release+notes
136. Et après ?
Groovy3 !
Nouveau « MOP »
Nouvellegrammaire Antlr v4
Support des lambdasdeJava8
144. Conclusion—1/2
• Un écosystème riche et fleurissant
• Groovy 2.0
• plus de modularité
• un thème « statique »
• vérification statique de type
• compilation statique
• un thème JDK 7
• support de invoke dynamic
• syntaxe project coin
145. Conclusion—2/2
• Groovy 2.1
• support complet de invoke dynamic
• @DelegatesTo
• extension du type checker pour les DSLSs
• méta-annotations
• Et au-delà...
• un nouveau MOP (Meta-Object Protocol)
• une nouvelle grammaire avec Antlr v4
• le support des lambdas de JDK 8