The document discusses new features and enhancements in Groovy 2, including modularity improvements, extension modules, Java 7 support like invoke dynamic and binary literals, and static type checking. Modularity changes allow Groovy to be split into a smaller core JAR and optional modules. Extension modules allow contributing new methods. Static type checking adds compile-time checks for errors.
19. Groovy modularity
• The « groovy-all » weighted... 6 MB !
• In addition to the language, we have APIs:
– template engine,Ant task scripting, Swing UI builder,
JMX builder...
• We want a lighter « core »
– with APIs in the form of modules
• Ability to wire in « extension methods »
16
20. The new JARs
• One smaller core JAR of 3 MB
• Modules
– console
– docgenerator
– groovydoc
– groovysh
– ant
– bsf
– jsr-223
– jmx
– sql
– swing
– servlet
– templates
– test
– testng
– json
– xml
17
21. The new JARs
• One smaller core JAR of 3 MB
• Modules
– console
– docgenerator
– groovydoc
– groovysh
– ant
– bsf
– jsr-223
– jmx
– sql
– swing
– servlet
– templates
– test
– testng
– json
– xml
17
28. Binary literals
• In addition to decimal, octal and hexa
• A new binary representation:
int
x
=
0b10101111
assert
x
==
175
byte
aByte
=
0b00100001
assert
aByte
==
33
int
anInt
=
0b1010000101000101
assert
anInt
==
41285
23
29.
30. Underscores in literals
• Use underscores in number literals
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
25
31. Multi-catch exception blocks
• A single catch block to catch several exceptions
at once, rather than duplicating blocks
try
{
/*
...
*/
}
catch(IOException
|
NullPointerException
e)
{
/*
un
seul
bloc
*/
}
26
33. JDK 7 Invoke Dynamic support
• A « flag » to compile with « indy »
– we might propose a backport for JDK < 7
• Avantages
– more runtime performance
•well... in theory...
– In the long term, we might replace
•« call site caching » ➔ MethodHandles
•« metaclass registry » ➔ ClassValues
– and the JIT « inlines » code more easily
28
34. A « static »
theme
Static type checking
Static compilation
35. Static type checking
• Goal: make the compiler grumpy!
– throw errors at compile-time
•rather than at runtime
30
38. Static type checking
• A « grumpy » compiler should...
– say when there’s a typo
in a method or variable name
– complain when a non-existent
method is called
– or on bad assignments
or use a bad return type
32
39. Static type checking
• The compiler should infer types...
– less explicit types and casts
– fine grained type inference
•« flow typing »
•« lowest upper bound »
33
40. Static type checking
• But the compiler should understand
extension methods
– allows a good level of dynamism,
despite the additional restrictions
34
44. Typos
import
groovy.transform.TypeChecked
void
method()
{}
@TypeChecked
test()
{
//
Cannot
find
matching
method
metthhoood()
metthhoood()
def
name
=
"Guillaume"
//
variable
naamme
is
undeclared
println
naamme
}
Compilation
error
Compilation
error
Annotation at the
method or class level
35
45. Wrong variable assignments
//
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'
36
46. Wrong variable assignments
//
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'
Compilation
error
36
47. Wrong variable assignments
//
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'
Compilation
error
Compilation
error
36
48. Wrong variable assignments
//
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'
Compilation
error
Compilation
error
Compilation
error
36
49. Wrong return type
//
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
}
37
50. Wrong return type
//
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
}
Compilation
error
37
51. Wrong return type
//
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
}
Compilation
error
In the end, call
StringBuilder’s toString()
37
52. Type inference
@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
}
}
38
53. Type inference
@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 optionally typed
38
54. Type inference
@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 optionally typed
Type String infered
38
55. Type inference
@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 optionally typed
trim() method added
dynamically by Groovy
Type String infered
38
56. Type inference
@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 optionally typed
Array element type inferred
trim() method added
dynamically by Groovy
Type String infered
38
57. Mix dynamic & statically checked code
@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()
}
39
58. Mix dynamic & statically checked code
@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()
}
Statically checked
39
59. Mix dynamic & statically checked code
@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()
}
Statically checked
Dynamic
39
60. Instanceof checks
@TypeChecked
void
test(Object
val)
{
if
(val
instanceof
String)
{
println
val.toUpperCase()
}
else
if
(val
instanceof
Number)
{
println
"X"
*
val.intValue()
}
}
40
61. Instanceof checks
@TypeChecked
void
test(Object
val)
{
if
(val
instanceof
String)
{
println
val.toUpperCase()
}
else
if
(val
instanceof
Number)
{
println
"X"
*
val.intValue()
}
}
No need
for casts
40
62. Instanceof checks
@TypeChecked
void
test(Object
val)
{
if
(val
instanceof
String)
{
println
val.toUpperCase()
}
else
if
(val
instanceof
Number)
{
println
"X"
*
val.intValue()
}
}
No need
for casts
No need
for casts
40
63. Instanceof checks
@TypeChecked
void
test(Object
val)
{
if
(val
instanceof
String)
{
println
val.toUpperCase()
}
else
if
(val
instanceof
Number)
{
println
"X"
*
val.intValue()
}
}
No need
for casts
No need
for castsUnderstand GDK’s method:
String#multiply(int)
40
64. Lowest Upper Bound
• The smallest common « super » type
– might be virtual (« non-denotable »)
@TypeChecked
test()
{
//
an
integer
and
a
BigDecimal
return
[1234,
3.14]
}
41
65. Lowest Upper Bound
• The smallest common « super » type
– might be virtual (« non-denotable »)
@TypeChecked
test()
{
//
an
integer
and
a
BigDecimal
return
[1234,
3.14]
}
Infered type:
List<T extends Number & Comparable & Serializable>
41
66. Flow typing
• Static type checking « follows » the type of values
assigned into variables
@TypeChecked
test()
{
def
var
=
123
//
int
infered
int
x
=
var
//
var
is
an
int
var
=
"123"
//
assign
a
String
into
var
x
=
var.toInteger()
//
no
cast
needed
var
=
123
x
=
var.toUpperCase()
//
error,
var
is
an
int
!
}
42
69. Static type checking and dynamic code
•Type checking happens at compile-time
– @TypeChecked doesn’t change behavior!
•do not confound with static compilation
• Most dynamic features can’t be checked
– metaclass changes, categories...
– dynamic variables from the « script binding »
• But compile-time metaprogramming OK
– if enough type information is available
44
70. But if it ain’t dynamic,
can we compile
it statically?
71. But if it ain’t dynamic,
can we compile
it statically?
But of course!!!
72. Static compilation
• Given the code is statically type checked,
lots of type information was infered...
so we can as well compile statically !
– ie. generate the same bytecode as javac
• Also interesting when stuck on JDK < 7
to gain performance improvements
46
73. Avantages of static compilation
• We gain:
– type safety
•thanks to static type checking
–the compiler builds upon it
– better performance
•close to Java’s performance
– code immune to « monkey patching »
•dynamic metaprogramming can interfere with your
framework’s code
– smaller generated bytecode
47
76. Drawbacks for static compilation
• We lose...
– Some dynamic features
•metaclass changes, categories
– Method « dynamic dispatch » can differ
•but thanks to type inference, it’s as close as «classical»
Groovy as possible
49
77. Mix statically compiled code with dynamic
@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()
}
50
78. Mix statically compiled code with dynamic
@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()
}
Statically compiled
50
79. Mix statically compiled code with dynamic
@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()
}
Statically compiled
Dynamic
50
80. Mix statically compiled code with dynamic
@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()
}
Statically compiled
Dynamic
Call a
method with
dynamic
content
50
81. Mix statically compiled code with dynamic
@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()
}
Statically compiled
Dynamic
Call a
method with
dynamic
content
Method signatures
are a contract!
50
82. What about performance?
• Comparisons between:
– Java
– Groovy
•with static compilation (Groovy 2.0)
•with primitive type optimization (Groovy 1.8)
•no optimization (Groovy 1.7)
51
83. What about performance?
Fibonacci
Pi (π)
quadrature
Binary
trees
Java
Static
compilation
Primitive
optimizations
No prim.
optimizations
191 ms 97 ms 3.6 s
197 ms 101 ms 4.3 s
360 ms 111 ms 23.7 s
2590 ms 3220 ms 50.0 s
1.71.82.x
52
95. @DelegatesTo annotation
• Static type checking works fine with a certain
range of DSLs
– « command chains », extension methods...
• But less for DSLs using closure delegation
– often used by DSLs like in Gradle
task
copyTask(type:
Copy)
{
from
'src/main/webapp'
into
'build/explodedWar'
}
59
99. @DelegatesTo annotation
class
ExecSpec
{
void
foo()
}
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c()
}
exec(spec)
{
foo()
}
The static type checker doesn’t
know about method foo()
60
100. @DelegatesTo annotation
class
ExecSpec
{
void
foo()
}
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c()
}
exec(spec)
{
foo()
}
Annotate with
@DelegatesTo(ExecSpec)
The static type checker doesn’t
know about method foo()
60
101. @DelegatesTo annotation
• With another
delegation strategy
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c.resolveStrategy
=
DELEGATE_FIRST
c()
}
61
102. @DelegatesTo annotation
• With another
delegation strategy
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c.resolveStrategy
=
DELEGATE_FIRST
c()
}
Annotate with
@DelegatesTo(value = ExecSpec,
strategy = DELEGATE_FIRST)
61
103. @DelegatesTo annotation
• Very interesting for DSLs using closure’s
delegation strategy
• Excellent for...
– documenting your APIs
– the integration within the IDE
•code completion, code navigation
– works well with static type checking and
static compilation
62
105. Extend the static type checker
• Extend the type checker to make it smarter!
– even smarter than Java’s! :-)
• By creating your own extension
@TypeChecked(extensions
=
'MyExtension.groovy')
void
exec()
{
//
code
to
be
further
checked...
}
64
106. Extend the static type checker
• Extend the type checker to make it smarter!
– even smarter than Java’s! :-)
• By creating your own extension
@TypeChecked(extensions
=
'MyExtension.groovy')
void
exec()
{
//
code
to
be
further
checked...
}
We could use a
meta-annotation
64
107. Extend the static type checker
• Help the static type checker when...
– impossible to infer types
– no matching method found
– no matching attribute found
– on wrong variable assignment
– ...
65
108. Extend the static type checker
• Your extension has access to an event-
oriented API
66
• onMethodSelection
• afterMethodCall
• beforeMethodCall
• afterVisitMethod
• beforeVisitMethod
• methodNotFound
• unresolvedVariable
• unresolvedProperty
• unresolvedAttribute
• incompatibleAssignment
111. Extend the static type checker
onMethodSelection
{
expr,
method
-‐>
...
}
afterMethodCall
{
mc
-‐>
...
}
unresolvedVariable
{
var
-‐>
...
}
methodNotFound
{
receiver,
name,
argList,
argTypes,
call
-‐>
...
}
incompatibleAssignment
{
lhsType,
rhsType,
expr
-‐>
...
}
MyExtension.groovy
Learn your
Groovy AST!
67
112. Extend the static type checker
onMethodSelection
{
expr,
method
-‐>
...
}
afterMethodCall
{
mc
-‐>
...
}
unresolvedVariable
{
var
-‐>
...
}
methodNotFound
{
receiver,
name,
argList,
argTypes,
call
-‐>
...
}
incompatibleAssignment
{
lhsType,
rhsType,
expr
-‐>
...
}
MyExtension.groovy
Learn your
Groovy AST!
No need to be
pre-compiled
67
113. Extend the static type checker
• A few examples
– check that a string is a valid SQL query
– check the arguments and types of sprintf() method
calls so they match the pattern
68
115. Compiler customization
• Groovy 1.8 introduced « customizers »
– add imports transparently
– apply AST transformations by default
– filter / secure scripts
• With the « static type checker » and « static
compilation », we were asked if we could apply
them by default
70
116. Compiler customization
• New options
– --basescript
to define a base script class for your scripts
– --configscript
to indicate a script to configure the
CompilerConfiguration object
71
117. Compiler customization
• Add the @ToString AST transformation
import
groovy.transform.ToString
import
org.codehaus.groovy.control.customizers
.ASTTransformationCustomizer
configuration.addCompilationCustomizer(
new
ASTTransformationCustomizer(ToString)
)
72
118. Compiler customization
• Add the @ToString AST transformation
import
groovy.transform.ToString
import
org.codehaus.groovy.control.customizers
.ASTTransformationCustomizer
configuration.addCompilationCustomizer(
new
ASTTransformationCustomizer(ToString)
)
CompilerConfiguration instance,
injected by default
72
119. Compiler customization
• A small DSL to configure the customization
configuration.customizers
{
//
apply
to
MyBean.groovy
source(basename:
'MyBean')
{
ast(ToString)
}
}
73
120. Compiler customization
• A small DSL to configure the customization
configuration.customizers
{
//
apply
to
MyBean.groovy
source(basename:
'MyBean')
{
ast(ToString)
}
}
configuration.customizers
{
//
apply
to
*.gbean
files
source(extension:
'.gbean')
{
ast(ToString)
}
}
73
121. Compiler customization
• A small DSL to configure the customization
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'
}
}
}
73
130. Implicit closure coercion
interface
Predicate<T>
{
boolean
test(T
t)
}
List<T>
filter(Predicate<T>
p)
Given a predicate & a List method to
filter according to that predicate...
78
131. Implicit closure coercion
interface
Predicate<T>
{
boolean
test(T
t)
}
List<T>
filter(Predicate<T>
p)
list.filter((it)
-‐>
it.age
>
18)
Given a predicate & a List method to
filter according to that predicate...
78
132. Implicit closure coercion
interface
Predicate<T>
{
boolean
test(T
t)
}
List<T>
filter(Predicate<T>
p)
list.filter((it)
-‐>
it.age
>
18)
Given a predicate & a List method to
filter according to that predicate...
Java 8 lambdas can be
more concise than Groovy!
78
133. Implicit closure coercion
interface
Predicate<T>
{
boolean
test(T
t)
}
List<T>
filter(Predicate<T>
p)
list.filter((it)
-‐>
it.age
>
18)
list.filter({
it.age
>
18
}
as
Predicate)
Given a predicate & a List method to
filter according to that predicate...
Java 8 lambdas can be
more concise than Groovy!
78
134. Implicit closure coercion
interface
Predicate<T>
{
boolean
test(T
t)
}
List<T>
filter(Predicate<T>
p)
list.filter((it)
-‐>
it.age
>
18)
list.filter
{
it.age
>
18
}
Given a predicate & a List method to
filter according to that predicate...
Java 8 lambdas can be
more concise than Groovy!
78
135. Implicit closure coercion
interface
Predicate<T>
{
boolean
test(T
t)
}
List<T>
filter(Predicate<T>
p)
list.filter((it)
-‐>
it.age
>
18)
list.filter
{
it.age
>
18
}
Given a predicate & a List method to
filter according to that predicate...
Java 8 lambdas can be
more concise than Groovy!
When no ambiguity,
make coercion implicit!
78
136. Implicit closure coercion
interface
Predicate<T>
{
boolean
test(T
t)
}
List<T>
filter(Predicate<T>
p)
list.filter((it)
-‐>
it.age
>
18)
list.filter
{
it.age
>
18
}
Given a predicate & a List method to
filter according to that predicate...
Java 8 lambdas can be
more concise than Groovy!
When no ambiguity,
make coercion implicit!
Go beyond Java, by making it
work on abstract classes too
78
137. DelegatingScript base script class
• Special base script class to delegate method
calls and property accesses to a delegatee
79
138. DelegatingScript base script class
• Special base script class to delegate method
calls and property accesses to a delegatee
Handy for
DSLs!
79
139. DelegatingScript base script class
• Special base script class to delegate method
calls and property accesses to a delegatee
Handy for
DSLs!
name
=
"Guillaume"
sayHi()
79
140. DelegatingScript base script class
• Special base script class to delegate method
calls and property accesses to a delegatee
class
Person
{
String
name
void
sayHi()
{
println
"Hi
$name"
}
}
Handy for
DSLs!
name
=
"Guillaume"
sayHi()
79
141. DelegatingScript base script class
• Special base script class to delegate method
calls and property accesses to a delegatee
class
Person
{
String
name
void
sayHi()
{
println
"Hi
$name"
}
}
Handy for
DSLs!
name
=
"Guillaume"
sayHi()
Use Person’s name property
79
142. DelegatingScript base script class
• Special base script class to delegate method
calls and property accesses to a delegatee
class
Person
{
String
name
void
sayHi()
{
println
"Hi
$name"
}
}
Handy for
DSLs!
name
=
"Guillaume"
sayHi()
Use Person’s name property
Call Person#sayHi()
79
143. DelegatingScript base script class
• Integration example:
def
cc
=
new
CompilerConfiguration()
cc.scriptBaseClass
=
DelegatingScript.class.name
def
sh
=
new
GroovyShell(cc)
def
script
=
sh.parse(file)
def
p
=
new
Person()
script.setDelegate(p)
script.run()
assert
p.name
==
"Guillaume"
80
144. DelegatingScript base script class
• Integration example:
def
cc
=
new
CompilerConfiguration()
cc.scriptBaseClass
=
DelegatingScript.class.name
def
sh
=
new
GroovyShell(cc)
def
script
=
sh.parse(file)
def
p
=
new
Person()
script.setDelegate(p)
script.run()
assert
p.name
==
"Guillaume"
Specify DelegatingScript base class
80
145. DelegatingScript base script class
• Integration example:
def
cc
=
new
CompilerConfiguration()
cc.scriptBaseClass
=
DelegatingScript.class.name
def
sh
=
new
GroovyShell(cc)
def
script
=
sh.parse(file)
def
p
=
new
Person()
script.setDelegate(p)
script.run()
assert
p.name
==
"Guillaume"
Specify DelegatingScript base class
Parse the script
80
146. DelegatingScript base script class
• Integration example:
def
cc
=
new
CompilerConfiguration()
cc.scriptBaseClass
=
DelegatingScript.class.name
def
sh
=
new
GroovyShell(cc)
def
script
=
sh.parse(file)
def
p
=
new
Person()
script.setDelegate(p)
script.run()
assert
p.name
==
"Guillaume"
Specify DelegatingScript base class
Parse the script
Define the delegate
80
147. DelegatingScript base script class
• Integration example:
def
cc
=
new
CompilerConfiguration()
cc.scriptBaseClass
=
DelegatingScript.class.name
def
sh
=
new
GroovyShell(cc)
def
script
=
sh.parse(file)
def
p
=
new
Person()
script.setDelegate(p)
script.run()
assert
p.name
==
"Guillaume"
Specify DelegatingScript base class
Parse the script
Define the delegate
Run the script
80
148. DelegatingScript base script class
• Integration example:
def
cc
=
new
CompilerConfiguration()
cc.scriptBaseClass
=
DelegatingScript.class.name
def
sh
=
new
GroovyShell(cc)
def
script
=
sh.parse(file)
def
p
=
new
Person()
script.setDelegate(p)
script.run()
assert
p.name
==
"Guillaume"
Specify DelegatingScript base class
Parse the script
Define the delegate
Run the script
Be Happy!
80
154. @Memoized transformation
• Piggypack on Closure’s own memoization
capabilities, but applied to methods
@Memoized
int
expensiveOp(int
a,
int
b)
{
sleep
1000
return
a
+
b
}
//
one
second
to
return
expensiveOp(1,
2)
//
immediate
result
returned
expensiveOp(1,
2)
83
155. Miscelanous improvements
• Precompiled type checking extensions
• Further tweaks to Groovysh with code
completion, better error reporting...
• Better syntax highlighting in Groovy Console
• Various dependency upgrades (Gradle,Ant)
@TypeChecked(extensions
=
'fqn.MyExtension')
84
156. Additional GDK methods...
• groupBy() on arrays
• combinations(Closure)
• collectMany() on Iterables
• JsonSlurper’s parse(File) and parse(URL)
assert
[[2,
3],
[4,
5,
6]]
.combinations
{
x,
y
-‐>
x*y
}
==
[8,
12,
10,
15,
12,
18]
85
161. Trait implementation
trait
FlyingAbility
{
String
fly()
{
"I
believe
I
can
fly!"
}
}
A trait keyword applying
the @Trait transformation
class
Car
implements
FlyingAbility
{}
87
162. Trait implementation
trait
FlyingAbility
{
String
fly()
{
"I
believe
I
can
fly!"
}
}
A trait keyword applying
the @Trait transformation
class
Car
implements
FlyingAbility
{}
A class «implements»
the trait
87
163. Trait implementation
trait
FlyingAbility
{
String
fly()
{
"I
believe
I
can
fly!"
}
}
A trait keyword applying
the @Trait transformation
class
Car
implements
FlyingAbility
{}
A class «implements»
the trait
def
c
=
new
Car()
assert
c.fly()
==
"I
believe
I
can
fly!"
87
166. New documentation and website
• New reference documentation
and guides using AsciiDoctor
• New website with a refreshed
skin and the new
content
89
172. Summary
• A very rich and blossoming ecosystem
•Groovy 2.0
– more modular
– a static theme
•static type checking
•static compilation
– JDK 7 theme
•Invoke Dynamic support
•Project Coin syntax enhancements
95
173. Summary
•Groovy 2.1
– Invoke Dynamic support completed
– @DelegatesTo annotation
– type checker extensions for DSLs
– meta-annotations
96