"Groovy 2.0 and beyond" presentation given at the Groovy/Grails eXchange conference.
Video can be seen here:
http://skillsmatter.com/podcast/groovy-grails/keynote-speech
2. Guillaume Laforge
• Groovy Project Manager at VMware
• Initiator of the Grails framework
• Creator of the Gaelyk
• Co-author of Groovy in Action
• Follow me on...
• My blog: http://glaforge.appspot.com
• Twitter: @glaforge
• Google+: http://gplus.to/glaforge
16. Groovy Modularity
• Groovy’s « all » JAR weighs in at 6 MB
• Nobody needs everything
• Template engine, Ant scripting, Swing UI building...
• Provide a smaller core
• and several smaller JARs per feature
• Provide hooks for setting up DGM methods,
17. The new JARs
• A smaller JAR: 3MB
• Modules
– console – jsr-223 – test
– docgenerator – jmx – testng
– groovydoc – sql – json
– groovysh – swing – xml
– ant – servlet
– bsf – templates
18. The new JARs
• A smaller JAR: 3MB
• Modules
– console – jsr-223 – test
– docgenerator – jmx – testng
– groovydoc – sql – json
– groovysh – swing – xml
– ant – servlet
– bsf – templates
25. Binary literals
• We had decimal, octal and hexadecimal
notations for number literals
• We can now use binary representations too
int
x
=
0b10101111
assert
x
==
175
byte
aByte
=
0b00100001
assert
aByte
==
33
int
anInt
=
0b1010000101000101
assert
anInt
==
41285
26. Underscore in literals
• Now we can also add underscores
in number literals for more readability
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
27. Multicatch
• One block for multiple exception caught
• rather than duplicating the block
try
{
/*
...
*/
}
catch(IOException
|
NullPointerException
e)
{
/*
one
block
to
treat
2
exceptions
*/
}
28. InvokeDynamic
• Groovy 2.0 supports JDK 7’s
invokeDynamic
• compiler has a flag for compiling against JDK 7
• might use the invokeDynamic backport for < JDK 7
• Benefits
• more runtime performance!
• at least as fast as current « dynamic » Groovy
• in the long run, will allow us to get rid of code!
• call site caching, thanks to MethodHandles
• metaclass registry, thanks to ClassValues
• will let the JIT inline calls more easily
29. Groovy 2.0 bird’s eye view
Java 7 Static Type
A more Project
Checking
modular Coin
Groovy Invoke Static
Compilation
Dynamic
31. Static Type Checking
• Goal: make the Groovy compiler « grumpy »!
• and throw compilation errors (not at runtime)
• Not everybody needs dynamic features
all the time
• think Java libraries scripting
• Grumpy should...
• tell you about your method or variable typos
• complain if you call methods that don’t exist
• shout on assignments of wrong types
• infer the types of your variables
• figure out GDK methods
32. Typos in a variable or method
import
groovy.transform.TypeChecked
void
method()
{}
@TypeChecked
test()
{
//
Cannot
find
matching
method
metthhoood()
metthhoood()
def
name
=
"Guillaume"
//
variable
naamme
is
undeclared
println
naamme
}
33. Typos in a variable or method
import
groovy.transform.TypeChecked
void
method()
{}
@TypeChecked
test()
{
//
Cannot
find
matching
method
metthhoood()
metthhoood()
Compilation
errors!
def
name
=
"Guillaume"
//
variable
naamme
is
undeclared
println
naamme
}
34. Typos in a variable or method
Annotation can be at
class or method level
import
groovy.transform.TypeChecked
void
method()
{}
@TypeChecked
test()
{
//
Cannot
find
matching
method
metthhoood()
metthhoood()
Compilation
errors!
def
name
=
"Guillaume"
//
variable
naamme
is
undeclared
println
naamme
}
35. Wrong assignments
//
cannot
assign
value
of
type...
to
variable...
int
x
=
new
Object()
Set
set
=
new
Object()
def
o
=
new
Object()
int
x
=
o
String[]
strings
=
['a','b','c']
int
str
=
strings[0]
//
cannot
find
matching
method
plus()
int
i
=
0
i
+=
'1'
36. Wrong assignments
//
cannot
assign
value
of
type...
to
variable...
int
x
=
new
Object()
Set
set
=
new
Object() Compilation
def
o
=
new
Object() errors!
int
x
=
o
String[]
strings
=
['a','b','c']
int
str
=
strings[0]
//
cannot
find
matching
method
plus()
int
i
=
0
i
+=
'1'
37. Wrong return types
//
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
}
38. Wrong return types
//
checks
if/else
branch
return
values
@TypeChecked
int
method()
{
Compilation
if
(true)
{
'String'
} error!
else
{
42
}
}
//
works
for
switch/case
&
try/catch/finally
//
transparent
toString()
implied
@TypeChecked
String
greeting(String
name)
{
def
sb
=
new
StringBuilder()
sb
<<
"Hi
"
<<
name
}
39. 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
}
}
40. Statically checked & dynamic methods
@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()
}
41. Instanceof checks
@TypeChecked
void
test(Object
val)
{
if
(val
instanceof
String)
{
println
val.toUpperCase()
}
else
if
(val
instanceof
Number)
{
println
"X"
*
val.intValue()
}
}
42. Instanceof checks
@TypeChecked
void
test(Object
val)
{
No need
if
(val
instanceof
String)
{
println
val.toUpperCase() for casts
}
else
if
(val
instanceof
Number)
{
println
"X"
*
val.intValue()
}
}
43. Instanceof checks
@TypeChecked
void
test(Object
val)
{
No need
if
(val
instanceof
String)
{
println
val.toUpperCase() for casts
}
else
if
(val
instanceof
Number)
{
println
"X"
*
val.intValue()
}
}
Can call String#multiply(int)
from the Groovy Development Kit
44. Lowest Upper Bound
• Represents the lowest « super » type
classes have in common
• may be virtual (aka « non-denotable »)
@TypeChecked
test()
{
//
an
integer
and
a
BigDecimal
return
[1234,
3.14]
}
45. Lowest Upper Bound
• Represents the lowest « super » type
classes have in common
• may be virtual (aka « non-denotable »)
@TypeChecked
test()
{
//
an
integer
and
a
BigDecimal
return
[1234,
3.14]
}
Inferred return type:
List<Number & Comparable>
46. Lowest Upper Bound
• Represents the lowest « super » type
classes have in common
• may be virtual (aka « non-denotable »)
@TypeChecked
test()
{
//
an
integer
and
a
BigDecimal
return
[1234,
3.14]
}
Inferred return type:
List<Number & Comparable>
47. Flow typing
• Static type checking shouldn’t complain
even for bad coding practicies
which work without type checks
@TypeChecked
test()
{
def
var
=
123
//
inferred
type
is
int
int
x
=
var
//
var
is
an
int
var
=
"123"
//
assign
var
with
a
String
x
=
var.toInteger()
//
no
problem,
no
need
to
cast
var
=
123
x
=
var.toUpperCase()
//
error,
var
is
int!
}
48. Gotcha: static checking vs dynamic
• Type checking works at compile-time
• adding @TypeChecked doesn’t change behavior
• do not confuse with static compilation
• Most dynamic features cannot be type checked
• metaclass changes, categories
• dynamically bound variables (ex: script’s binding)
• But compile-time metaprogramming works
• as long as proper type information is defined
50. Gotcha: runtime metaprogramming
@TypeChecked
void
test()
{
Integer.metaClass.foo
=
{}
123.foo()
}
Not allowed:
metaClass property
is dynamic
51. Gotcha: runtime metaprogramming
@TypeChecked
void
test()
{
Integer.metaClass.foo
=
{}
123.foo()
}
Method not Not allowed:
recognized metaClass property
is dynamic
52. Gotcha: explicit type for closures
@TypeChecked
test()
{
["a",
"b",
"c"].collect
{
it.toUpperCase()
//
Not
OK
}
}
53. Gotcha: explicit type for closures
@TypeChecked
test()
{
["a",
"b",
"c"].collect
{
String
it
-‐>
it.toUpperCase()
//
OK,
it’s
a
String
}
}
54. Closure shared variables
@TypeChecked
test()
{
def
var
=
"abc"
def
cl
=
{
var
=
new
Date()
}
if
(random)
cl()
var.toUpperCase()
//
Not
OK!
}
55. Closure shared variables
var assigned in the closure:
« shared closure variable »
@TypeChecked
test()
{
def
var
=
"abc"
def
cl
=
{
var
=
new
Date()
}
if
(random)
cl()
var.toUpperCase()
//
Not
OK!
}
56. Closure shared variables
var assigned in the closure:
« shared closure variable »
@TypeChecked
test()
{
Impossible to
def
var
=
"abc"
ensure the
def
cl
=
{
var
=
new
Date()
}
assignment
if
(random)
cl()
really happens
var.toUpperCase()
//
Not
OK!
}
57. Closure shared variables
var assigned in the closure:
« shared closure variable »
@TypeChecked
test()
{
Impossible to
def
var
=
"abc"
ensure the
def
cl
=
{
var
=
new
Date()
}
assignment
if
(random)
cl()
really happens
var.toUpperCase()
//
Not
OK!
}
Only methods of the most specific compatible
type (LUB) are allowed by the type checker
58. Closure shared variables
class
A
{
void
foo()
{}
}
class
B
extends
A
{
void
bar()
{}
}
@TypeChecked
test()
{
def
var
=
new
A()
def
cl
=
{
var
=
new
B()
}
if
(random)
cl()
var.foo()
//
OK!
}
59. Closure shared variables
class
A
{
void
foo()
{}
}
class
B
extends
A
{
void
bar()
{}
}
@TypeChecked
test()
{
def
var
=
new
A()
def
cl
=
{
var
=
new
B()
}
if
(random)
cl()
var.foo()
//
OK!
}
var is at least an
instance of A
60. Static Compilation
• Given your Groovy code can be type checked...
we can as well compile it « statically »
• ie. generate the same byte code as javac
• Also interesting for those stuck in JDK < 7
to benefit from performance improvements
61. Static Compilation: advantages
• You gain:
• Type safety
• thanks to static type checking
• static compilation builds upon static type checking
• Faster code
• as close as possible to Java’s performance
• Code immune to « monkey patching »
• metaprogramming badly used can interfere with
framework code
• Smaller bytecode size
62. Static Compilation: disadvantages
• But you loose:
• Dynamic features
• metaclass changes, categories, etc.
• Dynamic method dispatch
• although as close as possible to « dynamic » Groovy
63. Statically compiled & dynamic methods
@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()
}
64. What about performance?
• Comparisons between:
• Java
• Groovy static compilation (Groovy 2.0)
• Groovy with primitive optimizations (Groovy 1.8+)
• Groovy without optimizations (Groovy 1.7)
65. What about performance?
Pi (π) Binary
Fibonacci
quadrature trees
Java 191 ms 97 ms 3.6 s
1.7 1.8 2.0
Static
compilation 197 ms 101 ms 4.3 s
Primitive
optimizations 360 ms 111 ms 23.7 s
No prim.
optimizations 2590 ms 3220 ms 50.0 s
68. Full invoke dynamic support Groovy
2.1
• In Groovy 2.0, not all call paths
were going through invoke dynamic calls
• essentially method calls only
• still used call site caching techniques
• On JDK 7 with the « indy » JAR,
Groovy 2.1 uses invoke dynamic
everywhere
• On JDK < 7, still uses call site caching
69. @DelegatesTo Groovy
2.1
• Static type checking works nicely
for certain Domain-Specific Languages
• command chains, extension methods, etc.
• But for changes of delegation within closures,
it’s not helping
• often used by DSLs like within Gradle
• Enters @DelegatesTo!
70. @DelegatesTo Groovy
2.1
class
ExecSpec
{
void
foo()
}
76. @DelegatesTo Groovy
2.1
• With a special delegation strategy
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c.resolveStrategy
=
DELEGATE_FIRST
c()
}
77. @DelegatesTo Groovy
2.1
• With a special delegation strategy
Annotate with:
@DelegatesTo(value = ExecSpec,
strategy = DELEGATE_FIRST)
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c.resolveStrategy
=
DELEGATE_FIRST
c()
}
78. @DelegatesTo Groovy
2.1
• Use Target to specify the precise
argument to delegate to
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c()
}
79. @DelegatesTo Groovy
2.1
• Use Target to specify the precise
argument to delegate to
@DelegatesTo.Target("id")
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c()
}
80. @DelegatesTo Groovy
2.1
• Use Target to specify the precise
argument to delegate to
@DelegatesTo.Target("id") @DelegatesTo(target = "id")
void
exec(ExecSpec
sp,
Closure
c)
{
c.delegate
=
sp
c()
}
81. @DelegatesTo Groovy
2.1
• For DSLs using Groovy closure delegation
• Great for...
• documenting APIs
• IDE integration
• code completion, code navigation...
• working nicely with static type checking
and static compilation
82. Custom type checking Groovy
2.1
• You can make Groovy’s static type checking
even smarter, with your own smarts!
• even smarter than Java’s :-)
• Create your own type checker extension
@TypeChecked(extensions
=
'MyExtension.groovy')
void
exec()
{
//
code
to
be
further
checked...
}
83. Custom type checking Groovy
2.1
• Your own extension has access
to an event-based API
• onMethodSelection • methodNotFound
• afterMethodCall • unresolvedVariable
• beforeMethodCall • unresolvedProperty
• unresolvedAttribute
• afterVisitMethod
• beforeVisitMethod • incompatibleAssignment
86. Custom type checking Groovy
2.1
Doesn’t need
MyExtension.groovy to be compiled
onMethodSelection
{
expr,
method
-‐>
...
}
afterMethodCall
{
mc
-‐>
...
}
unresolvedVariable
{
var
-‐>
...
}
methodNotFound
{
receiver,
name,
argList,
argTypes,
call
-‐>
...
}
methodNotFound
{
receiver,
name,
argList,
argTypes,
call
-‐>
...
}
incompatibleAssignment
{
lhsType,
rhsType,
expr
-‐>
...
}
Know your
Groovy AST
API well ;-)
87. Alias annotations Groovy
2.1
• Ability to create meta-annotations
combining and/or parameterizing
other annotations
• Works also for local AST transformations
and their annotations
89. Alias annotations Groovy
2.1
@Immutable
@ToString(excludes
=
["age"])
@AnnotationCollector
@interface
MyAlias
{} The « collector »
90. Alias annotations Groovy
2.1
Collected
@Immutable
@ToString(excludes
=
["age"]) annotations
@AnnotationCollector
@interface
MyAlias
{} The « collector »
91. Alias annotations Groovy
2.1
Collected
@Immutable
@ToString(excludes
=
["age"]) annotations
@AnnotationCollector
@interface
MyAlias
{} The « collector »
Your own custom
alias name
92. Alias annotations Groovy
2.1
Collected
@Immutable
@ToString(excludes
=
["age"]) annotations
@AnnotationCollector
@interface
MyAlias
{} The « collector »
Your own custom
alias name @MyAlias
class
Foo
{
String
name
int
age
}
93. Alias annotations Groovy
2.1
Collected
@Immutable
@ToString(excludes
=
["age"]) annotations
@AnnotationCollector
@interface
MyAlias
{} The « collector »
Your own custom
alias name @MyAlias
class
Foo
{
Use your
String
name
concise alias
int
age
}
94. Take control of your Groovy! Groovy
2.1
• Groovy 1.8 introduced compilation customizers
• add imports, AST xforms, secure the AST...
• With static type checking and static
compilation, we received feedback from people
wanting them applied « by default »
• Allow to instruct the groovyc compiler
with a configuration script
• groovyc -configurator compConf.groovy Foo.groovy
95. Take control of your Groovy! Groovy
2.1
• Add a default @ToString transform
import
groovy.transform.ToString
import
org.codehaus.groovy.control.customizers
•
.ASTTransformationCustomizer
config.addCompilationCustomizer(
new
ASTTransformationCustomizer(ToString)
)
96. Take control of your Groovy! Groovy
2.1
• Add a default @ToString transform
import
groovy.transform.ToString
import
org.codehaus.groovy.control.customizers
•
.ASTTransformationCustomizer
config.addCompilationCustomizer(
new
ASTTransformationCustomizer(ToString)
)
Implicit «config» variable representing
a CompilationConfiguration instance
97. Take control of your Groovy! Groovy
2.1
• You can use a builder syntax:
config.customizers
{
//
apply
to
MyBean.groovy
source(basename:
'MyBean')
{
ast(ToString)
}
}
98. Take control of your Groovy! Groovy
2.1
• You can use a builder syntax:
config.customizers
{
//
apply
to
MyBean.groovy
config.customizers
{
source(basename:
'MyBean')
{
//
apply
to
*.gbean
files
ast(ToString)
source(extension:
'.gbean')
{
}
ast(ToString)
}
}
}
99. Take control of your Groovy! Groovy
2.1
• You can use a builder syntax:
config.customizers
{
//
apply
to
MyBean.groovy
config.customizers
{
source(basename:
'MyBean')
{
//
apply
to
*.gbean
files
ast(ToString)
config.customizers
{
source(extension:
'.gbean')
{
}
//
custom
filter
logic
ast(ToString)
}
//
with
compilation
units
}
}
source(unitValidator:
{
unit
-‐>
...
})
{
ast(ToString)
}
}
124. Summary (1/2)
• As always, a rich and blossoming ecosystem
• Groovy 2.0
• more modularity
• static theme
• static type checking
• static compilation
• JDK 7 theme
• invoke dynamic support
• project coin syntax enhancements
125. Summary (2/2)
• Groovy 2.1
• complete indy support
• @DelegatesTo
• custom type checking for your DSLs
• alias annotation
• And beyond...
• a new MOP (Meta-Object Protocol)
• a new grammar with Antlr 4
• JDK 8 lambda support
126. Thank you!
ge
me Lafor lopment
Guillau roovy Deve
G
H ead of m
@g mail.co
laforge rge
Email: g @glafo /glaforg
e
: .to
Twitter : http://gplus spot.com
+ pp
Google p://glaforge.a
tt
Blog: h
127. Picture credits
• London
http://www.londonup.com/media/1251/Tower-Bridge-london-582331_1024_768.jpg
• cherry blossom
http://wallpaperswide.com/cherry_blossom_3-wallpapers.html
• NKOTB
http://images1.fanpop.com/images/photos/2300000/nkotb-new-kids-on-the-block-2314664-1280-960.jpg
• USA today
http://www.adams-pr.com/images/uploads/USA_Today_logo.jpg
• back to the future
http://davidhiggerson.files.wordpress.com/2012/02/back-to-the-future-delorean.jpg
• magnifying glass
http://www.renders-graphiques.fr/image/upload/normal/loupe.png
• Santa Claus
http://icons.iconarchive.com/icons/fasticon/santa-claus/256/Happy-SantaClaus-icon.png
• Champagne
http://reallife101blogs.files.wordpress.com/2010/11/champagne_glasses2.jpg
• that’s all folks
http://4.bp.blogspot.com/-wJxosualm48/T4M_spcUUjI/AAAAAAAAB8E/njfLjNZQdsc/s1600/thats-all-folks.jpg
• MOP
http://imagethumbnails.milo.com/024/913/894/trimmed/24913521_25989894_trimmed.jpg
• grammar
http://edudemic.com/wp-content/uploads/2012/11/connected-learner-grammar.jpg