SlideShare a Scribd company logo
1 of 113
SPRINGONE2GX
WASHINGTON, DC
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution -NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Groovy AST Transformations
Dr Paul King
@paulk_asert
http://slideshare.net/paulk_asert/groovy-transforms
https://github.com/paulk-asert/groovy-transforms
Apache Groovy
Groovy in Action 2nd edition
Manning promo code: ctwspringo2gx
http://www.manning.com/koenig2
2
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Contents
3
 Introduction
• Built-in AST Transforms
• Writing your own transforms
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Parsing Summary
4
public run()
...
L1
ALOAD 1
LDC 1
AALOAD
ALOAD 0
LDC "Howdy Y'all"
INVOKEINTERFACE callCurrent()
ARETURN
...
println "Howdy Y'all"
BlockStatement
-> ReturnStatement
-> MethodCallExpression
-> VariableExpression("this")
-> ConstantExpression("println")
-> ArgumentListExpression
-> ConstantExpression("Howdy Y'all")
MyScript.groovy
> groovy MyScript.groovy
> groovyc MyScript.groovy
> groovysh
> groovyConsole
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 5
Parsing Summary
• 9 phase compiler
– Early stages: read source code
and convert into a sparse syntax
tree
– Middle stages: iteratively build up a
more dense and information rich
version of the syntax tree
– Later stages: check the tree and
convert it into byte code/class files
Initialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 6
Parsing - Early StagesInitialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
@ToString
class Greeter {
String message = "Howdy Y'all"
void greet() {
println message
}
}
ClassNode: Greeter
MethodNode: greet
Property: message
type: unresolved(String)
AnnotationNode: ToString
type: unresolved(ToString)
methods:
properties:
annotations:
BlockStatement
MethodCall: this.println(message)
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 7
Parsing - Middle StagesInitialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
ClassNode: Greeter
MethodNode: greet
FieldNode: message
type: resolved(String)
methods:
fields:
constructors:
ConstructorNode
MethodNode: getMessage
MethodNode: setMessage
MethodNode: toString
MethodNode: getMetaClass
…
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 8
Parsing - Final StagesInitialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
public greet()V
...
L1
...
ALOAD 0
GETFIELD Greeter.message
INVOKEINTERFACE callCurrent()
POP
...
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 9
When are transforms
applied?
Initialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
Local Transformations
Global Transformations
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 10
1 + 2 + 3
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 11
1 + 2 + 3
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 12
1 + 2 + 3
assert 1 + 1 == 2
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Contents
13
• Introduction
 Built-in AST Transforms
• Writing your own transforms
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 14
@ToString
@groovy.transform.ToString
class Detective {
String firstName, lastName
}
def d = new Detective(firstName: 'Sherlock', lastName: 'Holmes')
assert d.toString() == 'Detective(Sherlock, Holmes)'
class Detective {
String firstName, lastName
String toString() {
def _result = new StringBuilder()
_result.append('Detective(')
_result.append(this.firstName)
_result.append(', ')
_result.append(this.lastName)
_result.append(')')
return _result.toString()
}
}
ToStringASTTransformation
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 15
@ToString annotation parameters…
Parameter Name Purpose
excludes
Name of properties (list or comma separated) to
exclude from toString(). By default, all properties
are included. Incompatible with “includes”.
includes Name of properties (list or comma separated) to include
in toString(). Incompatible with “excludes”.
includeSuper Include the toString() for the super class by setting
to true. Default: false.
includeNames Include the names of the properties by setting this
parameter to true. Default: false.
allProperties Whether JavaBean "properties" are included.2.5
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 16
…@ToString annotation parameters
Parameter Name Purpose
includeFields Include fields, not just properties. Default: false.
ignoreNulls Exclude any properties that are null. By default
null values will be included.
includePackage
Print just the simple name of the class without the
package. By default the package name is
included.
cache
Set to true to cache toString() calculations.
Use only for immutable objects. By default the
value is recalculated upon each method call.
includeSuperProperties Include properties from super class. Default: false
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 17
@ToString (using parameters)
@ToString(ignoreNulls=true, excludes='lastName', includeNames=true,
includePackage=false, includeFields=true)
class Detective {
String firstName, lastName
List clues
private nemesis = 'Moriarty'
}
def d = new Detective(firstName: 'Sherlock', lastName: 'Holmes')
assert d.toString() ==
'Detective(firstName:Sherlock, nemesis:Moriarty)'
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 18
@EqualsAndHashCode
@EqualsAndHashCode
class Actor {
String firstName, lastName
}
def a1 = new Actor(firstName: 'Ian', lastName: 'McKellen')
def a2 = new Actor(firstName: 'Ian', lastName: 'McKellen')
assert !(a1.is(a2))
assert a1 == a2
EqualsAndHashCodeASTTransformation
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 19
@EqualsAndHashCode
class Actor {
String firstName, lastName
int hashCode() {
def _result = HashCodeHelper.initHash()
_result = HashCodeHelper.updateHash(_result, this.firstName)
_result = HashCodeHelper.updateHash(_result, this.lastName)
return _result
}
boolean canEqual(other) {
return other instanceof Actor
}
...
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 20
...
boolean equals(other) {
if (other == null) return false
if (this.is(other)) return true
if (!( other instanceof Actor)) return false
Actor otherTyped = (Actor) other
if (!(otherTyped.canEqual(this))) return false
if (!(this.getFirstName().is(otherTyped.getFirstName()))) {
if (!(this.getFirstName() == otherTyped.getFirstName())) {
return false
}
}
if (!(this.getLastName().is(otherTyped.getLastName()))) {
if (!(this.getLastName() == otherTyped.getLastName())) {
return false
}
}
return true
}
}
@EqualsAndHashCod
e
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 21
@EqualsAndHashCode annotation
parameters…Parameter Name Purpose
excludes
Exclude certain properties from the calculation by specifying
them as a comma separated list or literal list of String name
values. This is commonly used with an object that has an 'id'
field. By default, no properties are excluded. Incompatible with
“includes”
includes
Include only a specified list of properties by specifying them as a
comma separated list or literal list of String name values.
Incompatible with “excludes”.
cache
Set to true to cache hashCode() calculations. Use only for
immutable objects. By default the hashCode() is recalculated
whenever the hashCode() method is called.
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 22
…@EqualsAndHashCode annotation
parametersParameter Name Purpose
callSuper
Include properties from the super class by setting this parameter
to true. By default, the super class is not used as part of the
calculation.
includeFields
Include the class's fields, not just the properties, in the
calculation by setting this parameter to true. By default, fields
are not taken into account.
useCanEqual
Set to false to disable generation of a canEqual() method to
be used by equals(). By default the canEqual() method is
generated. The canEqual idiom provides a mechanism for
permitting equality in the presence of inheritance hierarchies.
For immutable classes with no explicit super class, this idiom is
not required.
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 23
@MapConstructor
• How does Groovy normally handle "named" arguments
class Athlete {
String first, last
}
def a1 = new Athlete(
first: 'Michael',
last: 'Jordan')
assert a1.first == 'Michael'
2.5
def a1 = new Athlete()
a1.setFirst('Michael')
a1.setLast('Jordan')
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 24
@MapConstructor
• With @MapConstructor we have an explicit Map constructor
• Good for Java integration & final fields
@MapConstructor
class Athlete {
String first, last
}
def a1 = new Athlete(
first: 'Michael',
last: 'Jordan')
assert a1.first == 'Michael'
2.5
Athlete(Map m) {
if (m.containsKey('first')) {
first = m.get('first')
}
if (m.containsKey('last')) {
last = m.get('last')
}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 25
@MapConstructor annotation parameters…
Parameter Name Purpose
excludes
Exclude certain properties from the constructor. Incompatible
with “includes”
includes
Include only a specified list of properties in the constructor.
Incompatible with “excludes”
pre
A Closure containing statements which will be prepended to the
generated constructor
post
A Closure containing statements which will be appended to the
end of the generated constructor
2.5
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 26
…@MapConstructor annotation parameters
Parameter Name Purpose
includeFields
Include the class's fields, not just the properties, in the
calculation by setting this parameter to true. By default, fields
are not taken into account.
includeProperties Include properties in the constructor. Default: true
includeSuperProperties Include properties from super classes in the constructor
useSetters
By default, properties are set directly using their respective
field but a setter can be used instead if desired
2.5
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 27
@MapConstructor
import groovy.transform.*
@ToString(includeFields=true, includeNames=true)
@MapConstructor(post={ full = "$first $last" })
class Person {
final String first, last
private final String full
}
assert new Person(first: 'Dierk', last: 'Koenig').toString() ==
'Person(first:Dierk, last:Koenig, full:Dierk Koenig)'
2.5
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 28
@TupleConstructor
• A traditional positional arguments constructor
@TupleConstructor
class Athlete {
String first, last
}
def a1 = new Athlete('Michael', 'Jordan')
def a2 = new Athlete('Michael')
def a3 = new Athlete(first: 'Michael')
assert a1.first == a2.first
assert a2.first == a3.first
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 29
@TupleConstructor annotation parameters…
Parameter Name Purpose
excludes
Exclude certain properties from the constructor. Incompatible
with “includes”
includes
Include only a specified list of properties in the constructor.
Incompatible with “excludes”
callSuper
Should super properties be called within a call to the parent
constructor rather than set as properties
force
By default, this annotation becomes a no-op if you provide your
own constructor, this flag forces constructor creation
defaults
Used to set whether default value processing is enabled (the
default) or disabled
2.5
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 30
…@TupleConstructor annotation parameters
Parameter Name Purpose
includeFields
Include the class's fields, not just the properties, in the
calculation by setting this parameter to true. By default, fields
are not taken into account.
includeProperties Include properties in the constructor. Default: true
includeSuperFields Include fields from super classes in the constructor
includeSuperProperties Include properties from super classes in the constructor
useSetters
By default, properties are set directly using their respective
field but a setter can be used instead if desired
2.5
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 31
@TupleConstructor
@TupleConstructor
class Athlete {
String first, last
}
def a1 = new Athlete('Michael', 'Jordan')
def a2 = new Athlete('Michael')
def a3 = new Athlete(first: 'Michael')
assert a1.first == a2.first
assert a2.first == a3.first
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 32
@TupleConstructor
@TupleConstructor
class Athlete {
String first, last
}
def a1 = new Athlete('Michael', 'Jordan')
def a2 = new Athlete('Michael')
def a3 = new Athlete(first: 'Michael')
assert a1.first == a2.first
assert a2.first == a3.first
class Athlete {
String first, last
Athlete(String first=null, String last=null) {…}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 33
@TupleConstructor
@TupleConstructor
class Athlete {
String first, last
}
def a1 = new Athlete('Michael', 'Jordan')
def a2 = new Athlete('Michael')
def a3 = new Athlete(first: 'Michael')
assert a1.first == a2.first
assert a2.first == a3.first
class Athlete {
String first, last
Athlete(String first=null, String last=null) {…}
}
class Athlete {
String first, last
Athlete(String first, String last) {…}
Athlete(String first) {…}
Athlete() {…}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 34
@TupleConstructor
@TupleConstructor(defaults=false)
class Athlete {
String first, last
}
def a1 = new Athlete('Michael', 'Jordan')
def a2 = new Athlete('Michael')
def a3 = new Athlete(first: 'Michael')
class Athlete {
String first, last
Athlete(String first, String last) {…}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 35
@Canonical
• Combines @ToString, @EqualsAndHashCode and
@TupleConstructor
• Actually uses AnnotationCollector in Groovy 2.5
@Canonical
class Inventor {
String firstName, lastName
}
def i1 = new Inventor('Thomas', 'Edison')
def i2 = new Inventor('Thomas')
assert i1 != i2
assert i1.firstName == i2.firstName
assert i1.toString() == 'Inventor(Thomas, Edison)'
2.5
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 36
@Canonical
• Combines @ToString, @EqualsAndHashCode and
@TupleConstructor
• Actually uses AnnotationCollector in Groovy 2.5
@AnnotationCollector(value=[ToString, TupleConstructor, EqualsAndHashCode],
mode=AnnotationCollectorMode.PREFER_EXPLICIT_MERGED)
public @interface Canonical { }
2.5
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 37
@Canonical
• Combines @ToString, @EqualsAndHashCode and
@TupleConstructor
• Actually uses AnnotationCollector in Groovy 2.5
@AnnotationCollector(value=[ToString, TupleConstructor, EqualsAndHashCode],
mode=AnnotationCollectorMode.PREFER_EXPLICIT_MERGED)
public @interface Canonical { }
@Canonical(includeNames=false)
@ToString(excludes='last', includeNames=true)
class Person {
String first, last
String getInitials() { "${first[0]}${last[0]}" }
}
assert new Person(first: 'Dierk', last: 'Koenig').toString() ==
'Person(first:Dierk, initials:DK)'
2.5
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 38
@AnnotationCollector
@AnnotationCollector
@ToString(excludes='id,first', includeNames=true)
@EqualsAndHashCode
@TupleConstructor(excludes='id')
@interface MyCanonical {}
@MyCanonical
class Person {
UUID id = UUID.randomUUID()
String first, last
}
def agent = new Person('James', 'Bond')
assert agent.toString() == 'Person(last:Bond)'
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 39
@Lazy…
• Safe and efficient deferred construction
• Understands double checked-locking and holder class idioms
// nominally expensive resource with stats
class Resource {
private static alive = 0
private static used = 0
Resource() { alive++ }
def use() { used++ }
static stats() { "$alive alive, $used used" }
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 40
…@Lazy class ResourceMain {
def res1 = new Resource()
@Lazy res2 = new Resource()
@Lazy static res3 = { new Resource() }()
@Lazy(soft=true) volatile Resource res4
}
new ResourceMain().with {
assert Resource.stats() == '1 alive, 0 used'
res2.use()
res3.use()
res4.use()
assert Resource.stats() == '4 alive, 3 used'
assert res4 instanceof Resource
def expected = 'res4=java.lang.ref.SoftReference'
assert it.dump().contains(expected)
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 41
@InheritConstructors
• Reduced boiler-plate for scenarios where parent classes have multiple
constructors (e.g. Exceptions & PrintWriter)
@InheritConstructors
class MyPrintWriter extends PrintWriter { }
def pw1 = new MyPrintWriter(new File('out1.txt'))
def pw2 = new MyPrintWriter('out2.txt', 'US-ASCII')
[pw1, pw2].each {
it << 'foo'
it.close()
}
assert new File('out1.txt').text == new
File('out2.txt').text
['out1.txt', 'out2.txt'].each{ new File(it).delete() }
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 42
@Sortable…
• Reduced boiler-plate for Comparable classes including specialised
Comparators
@Sortable(includes = 'last,initial')
class Politician {
String first
Character initial
String last
String initials() { first[0] + initial + last[0] }
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 43
…@Sortable
// @Sortable(includes = 'last,initial') class Politician { ... }
def politicians = [
new Politician(first: 'Margaret', initial: 'H', last: 'Thatcher'),
new Politician(first: 'George', initial: 'W', last: 'Bush')
]
politicians.with {
assert sort()*.initials() == ['GWB', 'MHT']
def comparator = Politician.comparatorByInitial()
assert toSorted(comparator)*.initials() == ['MHT', 'GWB']
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 44
@Builder
• Recent Java idiom is to use "fluent-api" builders
• Often not needed for Groovy
• But if you need Java integration or want improved IDE support…
@Builder
class Chemist {
String first, last
int born
}
def builder = Chemist.builder()
def c = builder.first("Marie").last("Curie").born(1867).build()
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 45
@Builder
• Supports customizable building strategies
DefaultStrategy Creates a nested helper class with methods for each property and a
build() method
SimpleStrategy Creates chainable setters, i.e. each setter returns the object itself
ExternalStrategy For creating a builder for a class you don’t have control over, e.g. from
a library or another team in your organization.
InitializerStrateg
y
Like DefaultStrategy but when used with @CompileStatic allows type-
safe object creation
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 46
@Delegate
class Clock {
void tellTime() { println '7 am' }
}
class Alarm {
void soundAlarm() { println 'Time to wake up' }
}
class AlarmClock {
@Delegate Clock c = new Clock()
@Delegate Alarm a = new Alarm()
}
new AlarmClock().with { tellTime(); soundAlarm() }
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 47
@Delegate
class Clock {
void tellTime() { println '7 am' }
}
class Alarm {
void soundAlarm() { println 'Time to wake up' }
}
class AlarmClock {
@Delegate Clock c = new Clock()
@Delegate Alarm a = new Alarm()
}
new AlarmClock().with { tellTime(); soundAlarm() }
class AlarmClock {
private Clock c = new Clock()
private Alarm a = new Alarm()
void tellTime() {
c.tellTime()
}
void soundAlarm() {
a.soundAlarm()
}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 48
@Memoized
• For making pure functions more efficient
class Calc {
def log = []
@Memoized
int sum(int a, int b) {
log << "$a+$b"
a + b
}
}
new Calc().with {
assert sum(3, 4) == 7
assert sum(4, 4) == 8
assert sum(3, 4) == 7
assert log.join(' ') == '3+4 4+4'
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 49
@TailRecursive
• For unravelling recursion
import groovy.transform.TailRecursive
class RecursiveCalc {
@TailRecursive
int accumulate(int n, int sum = 0) {
(n == 0) ? sum : accumulate(n - 1, sum + n)
}
}
new RecursiveCalc().with {
assert accumulate(10) == 55
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 50
@TailRecursiveclass RecursiveCalc {
int accumulate(int n, int sum) {
int _sum_ = sum
int _n_ = n
while (true) {
if (_n_ == 0) {
return _sum_
} else {
int __n__ = _n_
int __sum__ = _sum_
_n_ = __n__ - 1
_sum_ = __sum__ + __n__
}
}
}
int accumulate(int n) { accumulate(n, 0) }
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 51
@Immutable
• For unchanging data structures
@Immutable
class Genius {
String firstName, lastName
}
def g1 = new Genius(firstName: 'Albert', lastName: "Einstein")
assert g1.toString() == 'Genius(Albert, Einstein)'
def g2 = new Genius('Leonardo', "da Vinci")
assert g2.firstName == 'Leonardo'
shouldFail(ReadOnlyPropertyException) {
g2.lastName = 'DiCaprio'
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 52
@Log @Log4j @Log4j2 @Commons @Slf4j
• For easy logging
@groovy.util.logging.Log
class Database {
def search() {
log.fine(runLongDatabaseQuery())
}
def runLongDatabaseQuery() {
println 'Calling database'
/* ... */
return 'query result'
}
}
new Database().search()
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 53
@Synchronized
• For safe synchronization
class PhoneBook1 {
private final phoneNumbers = [:]
@Synchronized
def getNumber(key) {
phoneNumbers[key]
}
@Synchronized
void addNumber(key, value) {
phoneNumbers[key] = value
}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 54
@WithReadLock @WithWriteLock
• Declarative and efficient synchronization
class PhoneBook2 {
private final phoneNumbers = [:]
@WithReadLock
def getNumber(key) {
phoneNumbers[key]
}
@WithWriteLock
def addNumber(key, value) {
phoneNumbers[key] = value
}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 55
@AutoClone (Simple example)
• Easier cloning. With multiple styles supported: because one size doesn't fit
all for cloning on the JVM
@AutoClone
class Chef1 {
String name
List<String> recipes
Date born
}
def name = 'Heston Blumenthal'
def recipes = ['Snail porridge', 'Bacon & egg ice cream']
def born = Date.parse('yyyy-MM-dd', '1966-05-27')
def c1 = new Chef1(name: name, recipes: recipes, born: born)
def c2 = c1.clone()
assert c2.recipes == recipes
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 56
@AutoClone (Advanced example)
@TupleConstructor
@AutoClone(style=COPY_CONSTRUCTOR)
class Person {
final String name
final Date born
}
@TupleConstructor(includeSuperProperties=true, callSuper=true)
@AutoClone(style=COPY_CONSTRUCTOR)
class Chef2 extends Person {
final List<String> recipes
}
def name = 'Jamie Oliver'
def recipes = ['Lentil Soup', 'Crispy Duck']
def born = Date.parse('yyyy-MM-dd', '1975-05-27')
def c1 = new Chef2(name, born, recipes)
def c2 = c1.clone()
assert c2.name == name && c2.born == born && c2.recipes == recipes
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 57
@AutoCloneStyle
Style Description
CLONE
Adds a clone() method to your class. The clone() method will call
super.clone() before calling clone() on each Cloneable property of the class.
Doesn’t provide deep cloning. Not suitable if you have final properties. (Default)
SIMPLE
Adds a clone() method to your class which calls the no-arg constructor then copies
each property calling clone() for each Cloneable property. Handles inheritance
hierarchies. Not suitable if you have final properties. Doesn’t provide deep cloning.
COPY_CONSTRUCTOR
Adds a “copy” constructor, i.e. one which takes your class as its parameter, and a
clone() method to your class. The copy constructor method copies each property
calling clone() for each Cloneable property. The clone() method creates a new
instance making use of the copy constructor. Suitable if you have final properties.
Handles inheritance hierarchies. Doesn’t provide deep cloning.
SERIALIZATION
Adds a clone() method to your class which uses serialization to copy your class.
Suitable if your class already implements the Serializable or Externalizable
interface. Automatically performs deep cloning. Not as time or memory efficient. Not
suitable if you have final properties.
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 58
@AutoExternalize
@AutoExternalize
@ToString
class Composer {
String name
int born
boolean married
}
def c = new Composer(name: 'Wolfgang Amadeus Mozart',
born: 1756, married: true)
def baos = new ByteArrayOutputStream()
baos.withObjectOutputStream{ os -> os.writeObject(c) }
def bais = new ByteArrayInputStream(baos.toByteArray())
def loader = getClass().classLoader
def result
bais.withObjectInputStream(loader) { result = it.readObject().toString() }
assert result == 'Composer(Wolfgang Amadeus Mozart, 1756, true)'
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 59
@TimedInterrupt
@ThreadInterrupt
@ConditionalInterrupt
• For safer scripting
• Typically applied through compilation customizers
to user scripts rather than directly used
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Contents
60
• Introduction
• Built-in AST Transforms
 Writing your own transforms
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 61
Parsing Deep Dive
• Purpose
Read source files/streams and
configure compiler
• Key classes
CompilerConfiguration
CompilationUnit
Initialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 62
• Purpose
Use (ANTLR) grammar to convert source
code into token tree
• Key classes
CompilationUnit GroovyLexer
GroovyRecognizer GroovyTokenTypes
• CST Transforms
– http://java.dzone.com/articles/groovy-
antlr-plugins-better
Parsing Deep DiveInitialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 63
Parsing Deep Dive
• Purpose
Token tree converted into abstract syntax
tree (AST) and is the first place where we
can begin to write AST visitors
• Key classes
AntlrParserPlugin EnumVisitor
• AST Transforms
@Grab (global)
Initialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 64
Parsing Deep Dive
• Purpose
Resolves classes and performs
consistency and validity checks beyond
what the grammar can provide
• Key classes
StaticVerifier ResolveVisitor
StaticImportVisitor InnerClassVisitor,
AnnotationCollector
• AST Transforms
@Lazy @Builder @Field @Log
@Memoized @PackageScope
@TailRecursive @BaseScript
Initialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 65
Parsing Deep Dive
• Purpose
Finalizes the complete abstract syntax tree and typically
the last point at which you want to run a transformation
• Key classes
InnerClassCompletionVisitor EnumCompletionVisitor,
TraitComposer
• AST Transforms
@Bindable @Vetoable @Mixin @AutoClone
@ConditionalInterrupt @ThreadInterrupt
@TimedInterrupt @ListenerList @Canonical @Category
@Delegate @Bindable @Vetoable
@EqualsAndHashCode @AutoExternalize @Immutable
@IndexedProperty @Synchronized
@InheritConstructors @Sortable @WithReadLock
@WithWriteLock @Singleton @Newify @ToString
@TupleConstructor
Initialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 66
• Purpose
Chooses an instruction set for the
generated bytecode, e.g. Java 5 versus
pre-Java 5
• AST Transforms
@CompileStatic @TypeChecked
Parsing Deep DiveInitialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 67
Parsing Deep Dive
• Purpose
Creates bytecode based Class in memory
• Key classes
OptimizerVisitor GenericsVisitor Verifier
LabelVerifier ExtendedVerifier
ClassCompletionVerifier AsmClassGenerator
Initialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 68
Parsing Deep Dive
• Purpose
Binary output (.class file) written to file
system
Initialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 69
Parsing Deep Dive
• Purpose
Used to cleanup any resources no longer
needed
Initialization
Semantic Analysis
Instruction Selection
Parsing
Conversion
Canonicalization
Class Generation
Output
Finalization
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Visitor Pattern…
• separates the object being walked (the tree) from the behavior of the walker (the
visitor)
70
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
…Visitor Pattern
• Consider extending ClassCodeVisitorSupport but also consider extending
AbstractASTTransformation or using ClassCodeExpressionTransformer
71
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Writing a Local AST Transform…
• Create an Annotation
• Annotated with @GroovyASTTransformationClass
72
class MainTransformation{}
import org.codehaus.groovy.transform.GroovyASTTransformationClass
import java.lang.annotation.*
@Retention(RetentionPolicy.SOURCE)
@Target([ElementType.METHOD])
@GroovyASTTransformationClass(classes = [MainTransformation])
public @interface Main {}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
…Writing a Local AST Transform…
• Write your transform class
73
@GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)
class MainTransformation implements ASTTransformation {
private static final ClassNode[] NO_EXCEPTIONS =
ClassNode.EMPTY_ARRAY
private static final ClassNode STRING_ARRAY =
ClassHelper.STRING_TYPE.makeArray()
void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
// use guard clauses as a form of defensive programming
if (!astNodes) return
if (!astNodes[0] || !astNodes[1]) return
if (!(astNodes[0] instanceof AnnotationNode)) return
if (astNodes[0].classNode?.name != Main.class.name) return
if (!(astNodes[1] instanceof MethodNode)) return
...
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
…Writing a Local AST Transform…
74
...
MethodNode annotatedMethod = astNodes[1]
ClassNode declaringClass = annotatedMethod.declaringClass
def callMethod = callX(ctorX(declaringClass), annotatedMethod.name)
Statement body = block(stmt(callMethod))
def visibility = ACC_STATIC | ACC_PUBLIC
def parameters = params(param(STRING_ARRAY, 'args'))
declaringClass.addMethod('main', visibility, VOID_TYPE,
parameters, NO_EXCEPTIONS, body)
}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
…Writing a Local AST Transform
• Use the transform
75
class Greeter {
@Main
def greet() {
println "Hello from the greet() method!"
}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
…Writing a Local AST Transform
• Use the transform
76
class Greeter {
@Main
def greet() {
println "Hello from the greet() method!"
}
}
new GroovyShell(getClass().classLoader).evaluate
'''
class Greeter {
@Main
def greet() {
println "Hello from the greet() method!"
}
}
'''
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Creating AST…
• By hand (raw)
• Verbose, full IDE supported
77
import org.codehaus.groovy.ast.*
import org.codehaus.groovy.ast.stmt.*
import org.codehaus.groovy.ast.expr.*
new ReturnStatement(
new ConstructorCallExpression(
ClassHelper.make(Date),
ArgumentListExpression.EMPTY_ARGUMENTS
)
)
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
…Creating AST…
• By hand (with helper utility methods)
• Concise, not everything has concise form (yet)
78
import static org.codehaus.groovy.ast.tools.GeneralUtils.*
import static org.codehaus.groovy.ast.ClassHelper.*
returnS(ctorX(make(Date)))
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
…Creating AST…
• With ASTBuilder (from a specification/DSL)
• Requires AST knowledge, limited IDE support
79
import org.codehaus.groovy.ast.builder.AstBuilder
def ast = new AstBuilder().buildFromSpec {
returnStatement {
constructorCall(Date) {
argumentList {}
}
}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
…Creating AST…
• With ASTBuilder (from a String)
• Concise, intuitive, can't create everything, limited IDE support
80
import org.codehaus.groovy.ast.builder.AstBuilder
def ast = new AstBuilder().buildFromString('new Date()')
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
…Creating AST…
• With ASTBuilder (from code)
• Clear, concise, some entities cannot be created, IDE assistance
81
import org.codehaus.groovy.ast.builder.AstBuilder
def ast = new AstBuilder().buildFromCode {
new Date()
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
…Creating AST
• ASTBuilder limitations
• Great for prototyping, not always suitable for production transforms
• Groovy technology, can be slow, subject to global transforms
• Sometimes wasteful, e.g. you might need to create more than you need
such as creating a whole class to then pull out one method
• Some flavors don't support arbitrary node types, don't make it easy to
handle interactions with existing class nodes, don't make it easy to support
redirects or generics, nor allow you to properly set the line/column numbers
resulting in difficult to debug AST transform and cryptic compilation errors
82
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Feature Interactions
• Consider Groovy's @ToString annotation transform which runs at the end of the
Canonicalization phase
• Now suppose we want to create a @Trace annotation transform which when
placed on a class will "instrument" each method with "trace" println statements,
e.g. this:
becomes:
• What behaviour should I expect calling toString() if @Trace runs at the end of
Semantic Analysis? Canonicalization? Instruction Selection?
83
def setX(x) {
this.x = x
}
def setX(x) {
println "setX begin"
this.x = x
println "setX end"
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Testing AST Transforms
• Consider using ASTTest
• Test both the AST tree and the end-to-end behavior
• Consider writing defensive guards
• Use GroovyConsole
84
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Design Considerations
• Don't reuse ClassNodes
• Compile-time vs Runtime trade-offs and typing
• Feature Interactions/Fragility
• Beware Complexity
• Risks of introducing bugs
• Avoid global transforms (unless needed)
• GroovyConsole is your friend (AST/bytecode)
• Use addError for errors
• Retain line/column number information when transforming
• Watch variable scoping
85
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Further Information
• Documentation
• http://docs.groovy-lang.org/docs/groovy-
latest/html/documentation/#_compile_time_metaprogramming
• Macro Groovy
• https://github.com/bsideup/MacroGroovy
• https://github.com/bsideup/groovy-macro-methods
• AST Workshop
• http://melix.github.io/ast-workshop/
86
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
More Information: Groovy in Action, 2ed
87
Manning promo code: ctwspringo2gx
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/
Bonus Material
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 89
Groovy and "fluent-api" builders
• A common idiom in recent times for Java is to use an inner helper class and
accompanying "fluent-api" to reduce ceremony when creating Java classes
with many parameters
– But Groovy's named parameters greatly reduces this need
class Chemist {
String first
String last
int born
}
def c = new Chemist(first: "Marie", last: "Curie", born: 1867)
assert c.first == "Marie"
assert c.last == "Curie"
assert c.born == 1867
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 90
@Builder…
• But if you need Java integration or want improved IDE support…
import groovy.transform.builder.Builder
@Builder
class Chemist {
String first
String last
int born
}
def builder = Chemist.builder()
def c = builder.first("Marie").last("Curie").born(1867).build()
assert c.first == "Marie"
assert c.last == "Curie"
assert c.born == 1867
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 91
…@Builder…
• And it supports customizable building strategies
Strategy Description
DefaultStrategy
Creates a nested helper class for instance creation. Each method in the
helper class returns the helper until finally a build() method is called
which returns a created instance.
SimpleStrategy Creates chainable setters, i.e. each setter returns the object itself after
updating the appropriate property.
ExternalStrategy
Allows you to annotate an explicit builder class while leaving some buildee
class being built untouched. This is appropriate when you want to create a
builder for a class you don’t have control over, e.g. from a library or
another team in your organization.
InitializerStrategyCreates a nested helper class for instance creation which when used with
@CompileStatic allows type-safe object creation.
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 92
…@Builder
• Type-safe construction using phantom types (*if* you need it)
@Builder(builderStrategy=InitializerStrategy)
@Immutable
class Chemist {
String first, last
int born
}
@CompileStatic
def solution() {
def init = Chemist.createInitializer().first("Marie").last("Curie").born(1867)
new Chemist(init).with {
assert first == "Marie"
assert last == "Curie"
assert born == 1867
}
}
solution()
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 93
@Delegate (motivation)
• Anything wrong
with this?
class NoisySet extends HashSet {
@Override
boolean add(item) {
println "adding $item"
super.add(item)
}
@Override
boolean addAll(Collection items) {
items.each { println "adding $it" }
super.addAll(items)
}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 94
@Delegate (motivation)
• Anything wrong
with this?
• Could we fix this
implementation?
class NoisySet extends HashSet {
@Override
boolean add(item) {
println "adding $item"
super.add(item)
}
@Override
boolean addAll(Collection items) {
items.each { println "adding $it" }
super.addAll(items)
}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 95
@Delegate (motivation)
• Anything wrong
with this?
• Could we fix this
implementation?
• What about using
the delegate
pattern written
by hand?
class NoisySet extends HashSet {
@Override
boolean add(item) {
println "adding $item"
super.add(item)
}
@Override
boolean addAll(Collection items) {
items.each { println "adding $it" }
super.addAll(items)
}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 96
@Delegate
• For declarative but flexible use of the delegate pattern
class NoisySet {
@Delegate
Set delegate = new HashSet()
@Override
boolean add(item) {
println "adding $item"
delegate.add(item)
}
@Override
boolean addAll(Collection items) {
items.each { println "adding $it" }
delegate.addAll(items)
}
}
Set ns = new NoisySet()
ns.add(1)
ns.addAll([2, 3])
assert ns.size() == 3
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 97
@Delegate annotation parameters…
Parameter Name Purpose
interfaces Set this parameter to true to make the owner class implement the same
interfaces as the delegate, which is the default behavior. To make the
owner not implement the delegate interfaces, set this parameter to false.
deprecated
Set this parameter to true to have the owner class delegate methods
marked as @Deprecated in the delegate. By default @Deprecated
methods are not delegated.
methodAnnotations
Set to true if you want to carry over annotations from the methods of the
delegate to your delegating method. By default, annotations are not
carried over. Currently Closure annotation members are not supported.
parameterAnnotations
Set to true if you want to carry over annotations from the method
parameters of the delegate to your delegating method. By default,
annotations are not carried over. Currently Closure annotation members
are not supported.
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 98
…@Delegate annotation parameters
Only one of 'includes', 'includeTypes', 'excludes' or 'excludeTypes' should be used
Parameter Name Purpose
excludes List of method and/or property names to exclude when
delegating.
excludeTypes List of interfaces containing method signatures to exclude
when delegating.
includes List of method and/or property names to include when
delegating.
includeTypes List of interfaces containing method signatures to exclude
when delegating.
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 99
@Memoized
• For making pure functions more efficient
class Calc {
def log = []
@Memoized
int sum(int a, int b) {
log << "$a+$b"
a + b
}
}
new Calc().with {
assert sum(3, 4) == 7
assert sum(4, 4) == 8
assert sum(3, 4) == 7
assert log.join(' ') == '3+4 4+4'
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 100
@TailRecursive
• For unravelling recursion
import groovy.transform.TailRecursive
class RecursiveCalc {
@TailRecursive
int accumulate(int n, int sum = 0) {
(n == 0) ? sum : accumulate(n - 1, sum + n)
}
}
new RecursiveCalc().with {
assert accumulate(10) == 55
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 101
@TailRecursiveclass RecursiveCalc {
int accumulate(int n, int sum) {
int _sum_ = sum
int _n_ = n
while (true) {
if (_n_ == 0) {
return _sum_
} else {
int __n__ = _n_
int __sum__ = _sum_
_n_ = __n__ - 1
_sum_ = __sum__ + __n__
}
}
}
int accumulate(int n) { accumulate(n, 0) }
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 102
@Immutable
• For unchanging data structures
@Immutable
class Genius {
String firstName, lastName
}
def g1 = new Genius(firstName: 'Albert', lastName: "Einstein")
assert g1.toString() == 'Genius(Albert, Einstein)'
def g2 = new Genius('Leonardo', "da Vinci")
assert g2.firstName == 'Leonardo'
shouldFail(ReadOnlyPropertyException) {
g2.lastName = 'DiCaprio'
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 103
@Log @Log4j @Log4j2 @Commons @Slf4j
• For easy logging
@groovy.util.logging.Log
class Database {
def search() {
log.fine(runLongDatabaseQuery())
}
def runLongDatabaseQuery() {
println 'Calling database'
/* ... */
return 'query result'
}
}
new Database().search()
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 104
@Synchronized
• For safe synchronization
class PhoneBook1 {
private final phoneNumbers = [:]
@Synchronized
def getNumber(key) {
phoneNumbers[key]
}
@Synchronized
void addNumber(key, value) {
phoneNumbers[key] = value
}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 105
@WithReadLock @WithWriteLock
• Declarative and efficient synchronization
class PhoneBook2 {
private final phoneNumbers = [:]
@WithReadLock
def getNumber(key) {
phoneNumbers[key]
}
@WithWriteLock
def addNumber(key, value) {
phoneNumbers[key] = value
}
}
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 106
@AutoClone (Simple example)
• Easier cloning. With multiple styles supported: because one size doesn't fit
all for cloning on the JVM
@AutoClone
class Chef1 {
String name
List<String> recipes
Date born
}
def name = 'Heston Blumenthal'
def recipes = ['Snail porridge', 'Bacon & egg ice cream']
def born = Date.parse('yyyy-MM-dd', '1966-05-27')
def c1 = new Chef1(name: name, recipes: recipes, born: born)
def c2 = c1.clone()
assert c2.recipes == recipes
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 107
@AutoClone (Advanced example)
@TupleConstructor
@AutoClone(style=COPY_CONSTRUCTOR)
class Person {
final String name
final Date born
}
@TupleConstructor(includeSuperProperties=true, callSuper=true)
@AutoClone(style=COPY_CONSTRUCTOR)
class Chef2 extends Person {
final List<String> recipes
}
def name = 'Jamie Oliver'
def recipes = ['Lentil Soup', 'Crispy Duck']
def born = Date.parse('yyyy-MM-dd', '1975-05-27')
def c1 = new Chef2(name, born, recipes)
def c2 = c1.clone()
assert c2.name == name && c2.born == born && c2.recipes == recipes
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 108
@AutoCloneStyle
Style Description
CLONE
Adds a clone() method to your class. The clone() method will call
super.clone() before calling clone() on each Cloneable property of the class.
Doesn’t provide deep cloning. Not suitable if you have final properties. (Default)
SIMPLE
Adds a clone() method to your class which calls the no-arg constructor then copies
each property calling clone() for each Cloneable property. Handles inheritance
hierarchies. Not suitable if you have final properties. Doesn’t provide deep cloning.
COPY_CONSTRUCTOR
Adds a “copy” constructor, i.e. one which takes your class as its parameter, and a
clone() method to your class. The copy constructor method copies each property
calling clone() for each Cloneable property. The clone() method creates a new
instance making use of the copy constructor. Suitable if you have final properties.
Handles inheritance hierarchies. Doesn’t provide deep cloning.
SERIALIZATION
Adds a clone() method to your class which uses serialization to copy your class.
Suitable if your class already implements the Serializable or Externalizable
interface. Automatically performs deep cloning. Not as time or memory efficient. Not
suitable if you have final properties.
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 109
@AutoExternalize
@AutoExternalize
@ToString
class Composer {
String name
int born
boolean married
}
def c = new Composer(name: 'Wolfgang Amadeus Mozart',
born: 1756, married: true)
def baos = new ByteArrayOutputStream()
baos.withObjectOutputStream{ os -> os.writeObject(c) }
def bais = new ByteArrayInputStream(baos.toByteArray())
def loader = getClass().classLoader
def result
bais.withObjectInputStream(loader) { result = it.readObject().toString() }
assert result == 'Composer(Wolfgang Amadeus Mozart, 1756, true)'
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 110
@TimedInterrupt
@ThreadInterrupt
@ConditionalInterrupt
• For safer scripting
• Typically applied through compilation
customizers to user scripts rather than directly
used
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 111
@TimedInterrupt
@TimedInterrupt(value = 520L, unit = MILLISECONDS)
class BlastOff1 {
def log = []
def countdown(n) {
sleep 100
log << n
if (n == 0) log << 'ignition'
else countdown(n - 1)
}
}
def b = new BlastOff1()
Thread.start {
try {
b.countdown(10)
} catch (TimeoutException ignore) {
b.log << 'aborted'
}
}.join()
assert b.log.join(' ') == '10 9 8 7 6 aborted'
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 112
@ThreadInterrupt
@ThreadInterrupt
class BlastOff2 {
def log = []
def countdown(n) {
Thread.sleep 100
log << n
if (n == 0) log << 'ignition'
else countdown(n - 1)
}
}
def b = new BlastOff2()
def t1 = Thread.start {
try {
b.countdown(10)
} catch(InterruptedException ignore) {
b.log << 'aborted'
}
}
sleep 570
t1.interrupt()
t1.join()
assert b.log.join(' ') == '10 9 8 7 6 aborted'
Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a
Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 113
@ConditionalInterrupt
@ConditionalInterrupt({ count <= 5 })
class BlastOff3 {
def log = []
def count = 10
def countdown() {
while (count != 0) {
log << count
count--
}
log << 'ignition'
}
}
def b = new BlastOff3()
try {
b.countdown()
} catch (InterruptedException ignore) {
b.log << 'aborted'
}
assert b.log.join(' ') == '10 9 8 7 6 aborted'

More Related Content

What's hot

GR8Conf 2009: What's New in Groovy 1.6? by Guillaume Laforge
GR8Conf 2009: What's New in Groovy 1.6? by Guillaume LaforgeGR8Conf 2009: What's New in Groovy 1.6? by Guillaume Laforge
GR8Conf 2009: What's New in Groovy 1.6? by Guillaume LaforgeGR8Conf
 
Grooscript gr8conf
Grooscript gr8confGrooscript gr8conf
Grooscript gr8confGR8Conf
 
Little Helpers for Android Development with Kotlin
Little Helpers for Android Development with KotlinLittle Helpers for Android Development with Kotlin
Little Helpers for Android Development with KotlinKai Koenig
 
Building DSLs with Xtext - Eclipse Modeling Day 2009
Building DSLs with Xtext - Eclipse Modeling Day 2009Building DSLs with Xtext - Eclipse Modeling Day 2009
Building DSLs with Xtext - Eclipse Modeling Day 2009Heiko Behrens
 
Groovy Update - JavaPolis 2007
Groovy Update - JavaPolis 2007Groovy Update - JavaPolis 2007
Groovy Update - JavaPolis 2007Guillaume Laforge
 
Kotlin as a Better Java
Kotlin as a Better JavaKotlin as a Better Java
Kotlin as a Better JavaGarth Gilmour
 
Making Java Groovy (JavaOne 2013)
Making Java Groovy (JavaOne 2013)Making Java Groovy (JavaOne 2013)
Making Java Groovy (JavaOne 2013)Ken Kousen
 
Advanced Python, Part 1
Advanced Python, Part 1Advanced Python, Part 1
Advanced Python, Part 1Zaar Hai
 
Rust Workshop - NITC FOSSMEET 2017
Rust Workshop - NITC FOSSMEET 2017 Rust Workshop - NITC FOSSMEET 2017
Rust Workshop - NITC FOSSMEET 2017 pramode_ce
 
ConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with GroovyConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with GroovyIván López Martín
 
Java Keeps Throttling Up!
Java Keeps Throttling Up!Java Keeps Throttling Up!
Java Keeps Throttling Up!José Paumard
 
The Swift Compiler and Standard Library
The Swift Compiler and Standard LibraryThe Swift Compiler and Standard Library
The Swift Compiler and Standard LibrarySantosh Rajan
 
Java best practices
Java best practicesJava best practices
Java best practicesRay Toal
 

What's hot (20)

GR8Conf 2009: What's New in Groovy 1.6? by Guillaume Laforge
GR8Conf 2009: What's New in Groovy 1.6? by Guillaume LaforgeGR8Conf 2009: What's New in Groovy 1.6? by Guillaume Laforge
GR8Conf 2009: What's New in Groovy 1.6? by Guillaume Laforge
 
Clean coding-practices
Clean coding-practicesClean coding-practices
Clean coding-practices
 
Grooscript gr8conf
Grooscript gr8confGrooscript gr8conf
Grooscript gr8conf
 
Java Performance MythBusters
Java Performance MythBustersJava Performance MythBusters
Java Performance MythBusters
 
Getting rid of backtracking
Getting rid of backtrackingGetting rid of backtracking
Getting rid of backtracking
 
Little Helpers for Android Development with Kotlin
Little Helpers for Android Development with KotlinLittle Helpers for Android Development with Kotlin
Little Helpers for Android Development with Kotlin
 
Java Full Throttle
Java Full ThrottleJava Full Throttle
Java Full Throttle
 
Building DSLs with Xtext - Eclipse Modeling Day 2009
Building DSLs with Xtext - Eclipse Modeling Day 2009Building DSLs with Xtext - Eclipse Modeling Day 2009
Building DSLs with Xtext - Eclipse Modeling Day 2009
 
Groovy Update - JavaPolis 2007
Groovy Update - JavaPolis 2007Groovy Update - JavaPolis 2007
Groovy Update - JavaPolis 2007
 
Kotlin as a Better Java
Kotlin as a Better JavaKotlin as a Better Java
Kotlin as a Better Java
 
Making Java Groovy (JavaOne 2013)
Making Java Groovy (JavaOne 2013)Making Java Groovy (JavaOne 2013)
Making Java Groovy (JavaOne 2013)
 
Advanced Python, Part 1
Advanced Python, Part 1Advanced Python, Part 1
Advanced Python, Part 1
 
Rust Workshop - NITC FOSSMEET 2017
Rust Workshop - NITC FOSSMEET 2017 Rust Workshop - NITC FOSSMEET 2017
Rust Workshop - NITC FOSSMEET 2017
 
ConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with GroovyConFess Vienna 2015 - Metaprogramming with Groovy
ConFess Vienna 2015 - Metaprogramming with Groovy
 
Xtext Webinar
Xtext WebinarXtext Webinar
Xtext Webinar
 
Java Keeps Throttling Up!
Java Keeps Throttling Up!Java Keeps Throttling Up!
Java Keeps Throttling Up!
 
The Swift Compiler and Standard Library
The Swift Compiler and Standard LibraryThe Swift Compiler and Standard Library
The Swift Compiler and Standard Library
 
Java generics final
Java generics finalJava generics final
Java generics final
 
Groovy 2 and beyond
Groovy 2 and beyondGroovy 2 and beyond
Groovy 2 and beyond
 
Java best practices
Java best practicesJava best practices
Java best practices
 

Viewers also liked

awesome groovy
awesome groovyawesome groovy
awesome groovyPaul King
 
groovy databases
groovy databasesgroovy databases
groovy databasesPaul King
 
concurrency gpars
concurrency gparsconcurrency gpars
concurrency gparsPaul King
 
Groovy AST Transformations
Groovy AST TransformationsGroovy AST Transformations
Groovy AST Transformationshendersk
 
groovy rules
groovy rulesgroovy rules
groovy rulesPaul King
 
Going to Mars with Groovy Domain-Specific Languages
Going to Mars with Groovy Domain-Specific LanguagesGoing to Mars with Groovy Domain-Specific Languages
Going to Mars with Groovy Domain-Specific LanguagesGuillaume Laforge
 

Viewers also liked (6)

awesome groovy
awesome groovyawesome groovy
awesome groovy
 
groovy databases
groovy databasesgroovy databases
groovy databases
 
concurrency gpars
concurrency gparsconcurrency gpars
concurrency gpars
 
Groovy AST Transformations
Groovy AST TransformationsGroovy AST Transformations
Groovy AST Transformations
 
groovy rules
groovy rulesgroovy rules
groovy rules
 
Going to Mars with Groovy Domain-Specific Languages
Going to Mars with Groovy Domain-Specific LanguagesGoing to Mars with Groovy Domain-Specific Languages
Going to Mars with Groovy Domain-Specific Languages
 

Similar to groovy transforms

Grooscript in Action SpringOne2gx 2015
Grooscript in Action SpringOne2gx 2015Grooscript in Action SpringOne2gx 2015
Grooscript in Action SpringOne2gx 2015Jorge Franco Leza
 
Lessons learned from upgrading Thymeleaf
Lessons learned from upgrading ThymeleafLessons learned from upgrading Thymeleaf
Lessons learned from upgrading ThymeleafVMware Tanzu
 
Groovy 3 and the new Groovy Meta Object Protocol in examples
Groovy 3 and the new Groovy Meta Object Protocol in examplesGroovy 3 and the new Groovy Meta Object Protocol in examples
Groovy 3 and the new Groovy Meta Object Protocol in examplesGR8Conf
 
SpringOne 2GX 2015 - Fullstack Groovy developer
SpringOne 2GX 2015 - Fullstack Groovy developerSpringOne 2GX 2015 - Fullstack Groovy developer
SpringOne 2GX 2015 - Fullstack Groovy developerIván López Martín
 
Not Only Reactive - Data Access with Spring Data
Not Only Reactive - Data Access with Spring DataNot Only Reactive - Data Access with Spring Data
Not Only Reactive - Data Access with Spring DataVMware Tanzu
 
Simplifying Apache Geode with Spring Data
Simplifying Apache Geode with Spring DataSimplifying Apache Geode with Spring Data
Simplifying Apache Geode with Spring DataVMware Tanzu
 
What's new in Reactor Californium
What's new in Reactor CaliforniumWhat's new in Reactor Californium
What's new in Reactor CaliforniumStéphane Maldini
 
Java fx smart code econ
Java fx smart code econJava fx smart code econ
Java fx smart code econTom Schindl
 
Reactive Data Access with Spring Data
Reactive Data Access with Spring DataReactive Data Access with Spring Data
Reactive Data Access with Spring DataVMware Tanzu
 
7 Tips on Getting Your Theme Approved the First Time
7 Tips on Getting Your Theme Approved the First Time7 Tips on Getting Your Theme Approved the First Time
7 Tips on Getting Your Theme Approved the First TimeDmitry Mayorov
 
E(fx)clipse eclipse con
E(fx)clipse   eclipse conE(fx)clipse   eclipse con
E(fx)clipse eclipse conTom Schindl
 
Under the Hood of Reactive Data Access (1/2)
Under the Hood of Reactive Data Access (1/2)Under the Hood of Reactive Data Access (1/2)
Under the Hood of Reactive Data Access (1/2)VMware Tanzu
 
Flash Security, OWASP Chennai
Flash Security, OWASP ChennaiFlash Security, OWASP Chennai
Flash Security, OWASP Chennailavakumark
 
JavaOne 2008 - TS-5793 - Groovy and Grails, changing the landscape of Java EE...
JavaOne 2008 - TS-5793 - Groovy and Grails, changing the landscape of Java EE...JavaOne 2008 - TS-5793 - Groovy and Grails, changing the landscape of Java EE...
JavaOne 2008 - TS-5793 - Groovy and Grails, changing the landscape of Java EE...Guillaume Laforge
 
AEM Sightly Template Language
AEM Sightly Template LanguageAEM Sightly Template Language
AEM Sightly Template LanguageGabriel Walt
 
CICD With GitHub, Travis, SonarCloud and Docker Hub
CICD With GitHub, Travis, SonarCloud and Docker HubCICD With GitHub, Travis, SonarCloud and Docker Hub
CICD With GitHub, Travis, SonarCloud and Docker HubCarlos Cavero Barca
 

Similar to groovy transforms (20)

Grooscript in Action SpringOne2gx 2015
Grooscript in Action SpringOne2gx 2015Grooscript in Action SpringOne2gx 2015
Grooscript in Action SpringOne2gx 2015
 
Lessons learned from upgrading Thymeleaf
Lessons learned from upgrading ThymeleafLessons learned from upgrading Thymeleaf
Lessons learned from upgrading Thymeleaf
 
Groovy 3 and the new Groovy Meta Object Protocol in examples
Groovy 3 and the new Groovy Meta Object Protocol in examplesGroovy 3 and the new Groovy Meta Object Protocol in examples
Groovy 3 and the new Groovy Meta Object Protocol in examples
 
SpringOne 2GX 2015 - Fullstack Groovy developer
SpringOne 2GX 2015 - Fullstack Groovy developerSpringOne 2GX 2015 - Fullstack Groovy developer
SpringOne 2GX 2015 - Fullstack Groovy developer
 
Not Only Reactive - Data Access with Spring Data
Not Only Reactive - Data Access with Spring DataNot Only Reactive - Data Access with Spring Data
Not Only Reactive - Data Access with Spring Data
 
Simplifying Apache Geode with Spring Data
Simplifying Apache Geode with Spring DataSimplifying Apache Geode with Spring Data
Simplifying Apache Geode with Spring Data
 
What's new in Reactor Californium
What's new in Reactor CaliforniumWhat's new in Reactor Californium
What's new in Reactor Californium
 
Java fx smart code econ
Java fx smart code econJava fx smart code econ
Java fx smart code econ
 
Reactive Data Access with Spring Data
Reactive Data Access with Spring DataReactive Data Access with Spring Data
Reactive Data Access with Spring Data
 
7 Tips on Getting Your Theme Approved the First Time
7 Tips on Getting Your Theme Approved the First Time7 Tips on Getting Your Theme Approved the First Time
7 Tips on Getting Your Theme Approved the First Time
 
E(fx)clipse eclipse con
E(fx)clipse   eclipse conE(fx)clipse   eclipse con
E(fx)clipse eclipse con
 
Under the Hood of Reactive Data Access (1/2)
Under the Hood of Reactive Data Access (1/2)Under the Hood of Reactive Data Access (1/2)
Under the Hood of Reactive Data Access (1/2)
 
Flash Security, OWASP Chennai
Flash Security, OWASP ChennaiFlash Security, OWASP Chennai
Flash Security, OWASP Chennai
 
Os Secoske
Os SecoskeOs Secoske
Os Secoske
 
JavaOne 2008 - TS-5793 - Groovy and Grails, changing the landscape of Java EE...
JavaOne 2008 - TS-5793 - Groovy and Grails, changing the landscape of Java EE...JavaOne 2008 - TS-5793 - Groovy and Grails, changing the landscape of Java EE...
JavaOne 2008 - TS-5793 - Groovy and Grails, changing the landscape of Java EE...
 
Java 17
Java 17Java 17
Java 17
 
Bb Tequila Coding Style (Draft)
Bb Tequila Coding Style (Draft)Bb Tequila Coding Style (Draft)
Bb Tequila Coding Style (Draft)
 
J2ee standards > CDI
J2ee standards > CDIJ2ee standards > CDI
J2ee standards > CDI
 
AEM Sightly Template Language
AEM Sightly Template LanguageAEM Sightly Template Language
AEM Sightly Template Language
 
CICD With GitHub, Travis, SonarCloud and Docker Hub
CICD With GitHub, Travis, SonarCloud and Docker HubCICD With GitHub, Travis, SonarCloud and Docker Hub
CICD With GitHub, Travis, SonarCloud and Docker Hub
 

More from Paul King

functional groovy
functional groovyfunctional groovy
functional groovyPaul King
 
Agile Testing Practices
Agile Testing PracticesAgile Testing Practices
Agile Testing PracticesPaul King
 
groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expertPaul King
 
concurrency with GPars
concurrency with GParsconcurrency with GPars
concurrency with GParsPaul King
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrencyPaul King
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy PluginsPaul King
 
Dynamic Language Practices
Dynamic Language PracticesDynamic Language Practices
Dynamic Language PracticesPaul King
 
Make Your Builds More Groovy
Make Your Builds More GroovyMake Your Builds More Groovy
Make Your Builds More GroovyPaul King
 
Groovy Power Features
Groovy Power FeaturesGroovy Power Features
Groovy Power FeaturesPaul King
 
Make Your Testing Groovy
Make Your Testing GroovyMake Your Testing Groovy
Make Your Testing GroovyPaul King
 
Groovy Testing Sep2009
Groovy Testing Sep2009Groovy Testing Sep2009
Groovy Testing Sep2009Paul King
 
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...Paul King
 
Groovy Tutorial
Groovy TutorialGroovy Tutorial
Groovy TutorialPaul King
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Paul King
 
XML and Web Services with Groovy
XML and Web Services with GroovyXML and Web Services with Groovy
XML and Web Services with GroovyPaul King
 

More from Paul King (16)

functional groovy
functional groovyfunctional groovy
functional groovy
 
Agile Testing Practices
Agile Testing PracticesAgile Testing Practices
Agile Testing Practices
 
groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expert
 
concurrency with GPars
concurrency with GParsconcurrency with GPars
concurrency with GPars
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrency
 
GroovyDSLs
GroovyDSLsGroovyDSLs
GroovyDSLs
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy Plugins
 
Dynamic Language Practices
Dynamic Language PracticesDynamic Language Practices
Dynamic Language Practices
 
Make Your Builds More Groovy
Make Your Builds More GroovyMake Your Builds More Groovy
Make Your Builds More Groovy
 
Groovy Power Features
Groovy Power FeaturesGroovy Power Features
Groovy Power Features
 
Make Your Testing Groovy
Make Your Testing GroovyMake Your Testing Groovy
Make Your Testing Groovy
 
Groovy Testing Sep2009
Groovy Testing Sep2009Groovy Testing Sep2009
Groovy Testing Sep2009
 
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
 
Groovy Tutorial
Groovy TutorialGroovy Tutorial
Groovy Tutorial
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
 
XML and Web Services with Groovy
XML and Web Services with GroovyXML and Web Services with Groovy
XML and Web Services with Groovy
 

Recently uploaded

What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...confluent
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...OnePlan Solutions
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based projectAnoyGreter
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
cpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.pptcpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.pptrcbcrtm
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceBrainSell Technologies
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commercemanigoyal112
 
Sending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdfSending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdf31events.com
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Matt Ray
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEEVICTOR MAESTRE RAMIREZ
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringHironori Washizaki
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfDrew Moseley
 
Software Coding for software engineering
Software Coding for software engineeringSoftware Coding for software engineering
Software Coding for software engineeringssuserb3a23b
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Mater
 

Recently uploaded (20)

What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
 
MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based project
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
cpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.pptcpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.ppt
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. Salesforce
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commerce
 
Sending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdfSending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdf
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 
Advantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your BusinessAdvantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your Business
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their Engineering
 
Comparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdfComparing Linux OS Image Update Models - EOSS 2024.pdf
Comparing Linux OS Image Update Models - EOSS 2024.pdf
 
Software Coding for software engineering
Software Coding for software engineeringSoftware Coding for software engineering
Software Coding for software engineering
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)
 

groovy transforms

  • 1. SPRINGONE2GX WASHINGTON, DC Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution -NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Groovy AST Transformations Dr Paul King @paulk_asert http://slideshare.net/paulk_asert/groovy-transforms https://github.com/paulk-asert/groovy-transforms
  • 2. Apache Groovy Groovy in Action 2nd edition Manning promo code: ctwspringo2gx http://www.manning.com/koenig2 2
  • 3. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Contents 3  Introduction • Built-in AST Transforms • Writing your own transforms
  • 4. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Parsing Summary 4 public run() ... L1 ALOAD 1 LDC 1 AALOAD ALOAD 0 LDC "Howdy Y'all" INVOKEINTERFACE callCurrent() ARETURN ... println "Howdy Y'all" BlockStatement -> ReturnStatement -> MethodCallExpression -> VariableExpression("this") -> ConstantExpression("println") -> ArgumentListExpression -> ConstantExpression("Howdy Y'all") MyScript.groovy > groovy MyScript.groovy > groovyc MyScript.groovy > groovysh > groovyConsole
  • 5. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 5 Parsing Summary • 9 phase compiler – Early stages: read source code and convert into a sparse syntax tree – Middle stages: iteratively build up a more dense and information rich version of the syntax tree – Later stages: check the tree and convert it into byte code/class files Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization
  • 6. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 6 Parsing - Early StagesInitialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization @ToString class Greeter { String message = "Howdy Y'all" void greet() { println message } } ClassNode: Greeter MethodNode: greet Property: message type: unresolved(String) AnnotationNode: ToString type: unresolved(ToString) methods: properties: annotations: BlockStatement MethodCall: this.println(message)
  • 7. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 7 Parsing - Middle StagesInitialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization ClassNode: Greeter MethodNode: greet FieldNode: message type: resolved(String) methods: fields: constructors: ConstructorNode MethodNode: getMessage MethodNode: setMessage MethodNode: toString MethodNode: getMetaClass …
  • 8. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 8 Parsing - Final StagesInitialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization public greet()V ... L1 ... ALOAD 0 GETFIELD Greeter.message INVOKEINTERFACE callCurrent() POP ...
  • 9. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 9 When are transforms applied? Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization Local Transformations Global Transformations
  • 10. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 10 1 + 2 + 3
  • 11. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 11 1 + 2 + 3
  • 12. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 12 1 + 2 + 3 assert 1 + 1 == 2
  • 13. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Contents 13 • Introduction  Built-in AST Transforms • Writing your own transforms
  • 14. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 14 @ToString @groovy.transform.ToString class Detective { String firstName, lastName } def d = new Detective(firstName: 'Sherlock', lastName: 'Holmes') assert d.toString() == 'Detective(Sherlock, Holmes)' class Detective { String firstName, lastName String toString() { def _result = new StringBuilder() _result.append('Detective(') _result.append(this.firstName) _result.append(', ') _result.append(this.lastName) _result.append(')') return _result.toString() } } ToStringASTTransformation
  • 15. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 15 @ToString annotation parameters… Parameter Name Purpose excludes Name of properties (list or comma separated) to exclude from toString(). By default, all properties are included. Incompatible with “includes”. includes Name of properties (list or comma separated) to include in toString(). Incompatible with “excludes”. includeSuper Include the toString() for the super class by setting to true. Default: false. includeNames Include the names of the properties by setting this parameter to true. Default: false. allProperties Whether JavaBean "properties" are included.2.5
  • 16. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 16 …@ToString annotation parameters Parameter Name Purpose includeFields Include fields, not just properties. Default: false. ignoreNulls Exclude any properties that are null. By default null values will be included. includePackage Print just the simple name of the class without the package. By default the package name is included. cache Set to true to cache toString() calculations. Use only for immutable objects. By default the value is recalculated upon each method call. includeSuperProperties Include properties from super class. Default: false
  • 17. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 17 @ToString (using parameters) @ToString(ignoreNulls=true, excludes='lastName', includeNames=true, includePackage=false, includeFields=true) class Detective { String firstName, lastName List clues private nemesis = 'Moriarty' } def d = new Detective(firstName: 'Sherlock', lastName: 'Holmes') assert d.toString() == 'Detective(firstName:Sherlock, nemesis:Moriarty)'
  • 18. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 18 @EqualsAndHashCode @EqualsAndHashCode class Actor { String firstName, lastName } def a1 = new Actor(firstName: 'Ian', lastName: 'McKellen') def a2 = new Actor(firstName: 'Ian', lastName: 'McKellen') assert !(a1.is(a2)) assert a1 == a2 EqualsAndHashCodeASTTransformation
  • 19. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 19 @EqualsAndHashCode class Actor { String firstName, lastName int hashCode() { def _result = HashCodeHelper.initHash() _result = HashCodeHelper.updateHash(_result, this.firstName) _result = HashCodeHelper.updateHash(_result, this.lastName) return _result } boolean canEqual(other) { return other instanceof Actor } ...
  • 20. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 20 ... boolean equals(other) { if (other == null) return false if (this.is(other)) return true if (!( other instanceof Actor)) return false Actor otherTyped = (Actor) other if (!(otherTyped.canEqual(this))) return false if (!(this.getFirstName().is(otherTyped.getFirstName()))) { if (!(this.getFirstName() == otherTyped.getFirstName())) { return false } } if (!(this.getLastName().is(otherTyped.getLastName()))) { if (!(this.getLastName() == otherTyped.getLastName())) { return false } } return true } } @EqualsAndHashCod e
  • 21. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 21 @EqualsAndHashCode annotation parameters…Parameter Name Purpose excludes Exclude certain properties from the calculation by specifying them as a comma separated list or literal list of String name values. This is commonly used with an object that has an 'id' field. By default, no properties are excluded. Incompatible with “includes” includes Include only a specified list of properties by specifying them as a comma separated list or literal list of String name values. Incompatible with “excludes”. cache Set to true to cache hashCode() calculations. Use only for immutable objects. By default the hashCode() is recalculated whenever the hashCode() method is called.
  • 22. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 22 …@EqualsAndHashCode annotation parametersParameter Name Purpose callSuper Include properties from the super class by setting this parameter to true. By default, the super class is not used as part of the calculation. includeFields Include the class's fields, not just the properties, in the calculation by setting this parameter to true. By default, fields are not taken into account. useCanEqual Set to false to disable generation of a canEqual() method to be used by equals(). By default the canEqual() method is generated. The canEqual idiom provides a mechanism for permitting equality in the presence of inheritance hierarchies. For immutable classes with no explicit super class, this idiom is not required.
  • 23. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 23 @MapConstructor • How does Groovy normally handle "named" arguments class Athlete { String first, last } def a1 = new Athlete( first: 'Michael', last: 'Jordan') assert a1.first == 'Michael' 2.5 def a1 = new Athlete() a1.setFirst('Michael') a1.setLast('Jordan')
  • 24. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 24 @MapConstructor • With @MapConstructor we have an explicit Map constructor • Good for Java integration & final fields @MapConstructor class Athlete { String first, last } def a1 = new Athlete( first: 'Michael', last: 'Jordan') assert a1.first == 'Michael' 2.5 Athlete(Map m) { if (m.containsKey('first')) { first = m.get('first') } if (m.containsKey('last')) { last = m.get('last') } }
  • 25. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 25 @MapConstructor annotation parameters… Parameter Name Purpose excludes Exclude certain properties from the constructor. Incompatible with “includes” includes Include only a specified list of properties in the constructor. Incompatible with “excludes” pre A Closure containing statements which will be prepended to the generated constructor post A Closure containing statements which will be appended to the end of the generated constructor 2.5
  • 26. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 26 …@MapConstructor annotation parameters Parameter Name Purpose includeFields Include the class's fields, not just the properties, in the calculation by setting this parameter to true. By default, fields are not taken into account. includeProperties Include properties in the constructor. Default: true includeSuperProperties Include properties from super classes in the constructor useSetters By default, properties are set directly using their respective field but a setter can be used instead if desired 2.5
  • 27. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 27 @MapConstructor import groovy.transform.* @ToString(includeFields=true, includeNames=true) @MapConstructor(post={ full = "$first $last" }) class Person { final String first, last private final String full } assert new Person(first: 'Dierk', last: 'Koenig').toString() == 'Person(first:Dierk, last:Koenig, full:Dierk Koenig)' 2.5
  • 28. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 28 @TupleConstructor • A traditional positional arguments constructor @TupleConstructor class Athlete { String first, last } def a1 = new Athlete('Michael', 'Jordan') def a2 = new Athlete('Michael') def a3 = new Athlete(first: 'Michael') assert a1.first == a2.first assert a2.first == a3.first
  • 29. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 29 @TupleConstructor annotation parameters… Parameter Name Purpose excludes Exclude certain properties from the constructor. Incompatible with “includes” includes Include only a specified list of properties in the constructor. Incompatible with “excludes” callSuper Should super properties be called within a call to the parent constructor rather than set as properties force By default, this annotation becomes a no-op if you provide your own constructor, this flag forces constructor creation defaults Used to set whether default value processing is enabled (the default) or disabled 2.5
  • 30. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 30 …@TupleConstructor annotation parameters Parameter Name Purpose includeFields Include the class's fields, not just the properties, in the calculation by setting this parameter to true. By default, fields are not taken into account. includeProperties Include properties in the constructor. Default: true includeSuperFields Include fields from super classes in the constructor includeSuperProperties Include properties from super classes in the constructor useSetters By default, properties are set directly using their respective field but a setter can be used instead if desired 2.5
  • 31. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 31 @TupleConstructor @TupleConstructor class Athlete { String first, last } def a1 = new Athlete('Michael', 'Jordan') def a2 = new Athlete('Michael') def a3 = new Athlete(first: 'Michael') assert a1.first == a2.first assert a2.first == a3.first
  • 32. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 32 @TupleConstructor @TupleConstructor class Athlete { String first, last } def a1 = new Athlete('Michael', 'Jordan') def a2 = new Athlete('Michael') def a3 = new Athlete(first: 'Michael') assert a1.first == a2.first assert a2.first == a3.first class Athlete { String first, last Athlete(String first=null, String last=null) {…} }
  • 33. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 33 @TupleConstructor @TupleConstructor class Athlete { String first, last } def a1 = new Athlete('Michael', 'Jordan') def a2 = new Athlete('Michael') def a3 = new Athlete(first: 'Michael') assert a1.first == a2.first assert a2.first == a3.first class Athlete { String first, last Athlete(String first=null, String last=null) {…} } class Athlete { String first, last Athlete(String first, String last) {…} Athlete(String first) {…} Athlete() {…} }
  • 34. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 34 @TupleConstructor @TupleConstructor(defaults=false) class Athlete { String first, last } def a1 = new Athlete('Michael', 'Jordan') def a2 = new Athlete('Michael') def a3 = new Athlete(first: 'Michael') class Athlete { String first, last Athlete(String first, String last) {…} }
  • 35. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 35 @Canonical • Combines @ToString, @EqualsAndHashCode and @TupleConstructor • Actually uses AnnotationCollector in Groovy 2.5 @Canonical class Inventor { String firstName, lastName } def i1 = new Inventor('Thomas', 'Edison') def i2 = new Inventor('Thomas') assert i1 != i2 assert i1.firstName == i2.firstName assert i1.toString() == 'Inventor(Thomas, Edison)' 2.5
  • 36. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 36 @Canonical • Combines @ToString, @EqualsAndHashCode and @TupleConstructor • Actually uses AnnotationCollector in Groovy 2.5 @AnnotationCollector(value=[ToString, TupleConstructor, EqualsAndHashCode], mode=AnnotationCollectorMode.PREFER_EXPLICIT_MERGED) public @interface Canonical { } 2.5
  • 37. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 37 @Canonical • Combines @ToString, @EqualsAndHashCode and @TupleConstructor • Actually uses AnnotationCollector in Groovy 2.5 @AnnotationCollector(value=[ToString, TupleConstructor, EqualsAndHashCode], mode=AnnotationCollectorMode.PREFER_EXPLICIT_MERGED) public @interface Canonical { } @Canonical(includeNames=false) @ToString(excludes='last', includeNames=true) class Person { String first, last String getInitials() { "${first[0]}${last[0]}" } } assert new Person(first: 'Dierk', last: 'Koenig').toString() == 'Person(first:Dierk, initials:DK)' 2.5
  • 38. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 38 @AnnotationCollector @AnnotationCollector @ToString(excludes='id,first', includeNames=true) @EqualsAndHashCode @TupleConstructor(excludes='id') @interface MyCanonical {} @MyCanonical class Person { UUID id = UUID.randomUUID() String first, last } def agent = new Person('James', 'Bond') assert agent.toString() == 'Person(last:Bond)'
  • 39. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 39 @Lazy… • Safe and efficient deferred construction • Understands double checked-locking and holder class idioms // nominally expensive resource with stats class Resource { private static alive = 0 private static used = 0 Resource() { alive++ } def use() { used++ } static stats() { "$alive alive, $used used" } }
  • 40. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 40 …@Lazy class ResourceMain { def res1 = new Resource() @Lazy res2 = new Resource() @Lazy static res3 = { new Resource() }() @Lazy(soft=true) volatile Resource res4 } new ResourceMain().with { assert Resource.stats() == '1 alive, 0 used' res2.use() res3.use() res4.use() assert Resource.stats() == '4 alive, 3 used' assert res4 instanceof Resource def expected = 'res4=java.lang.ref.SoftReference' assert it.dump().contains(expected) }
  • 41. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 41 @InheritConstructors • Reduced boiler-plate for scenarios where parent classes have multiple constructors (e.g. Exceptions & PrintWriter) @InheritConstructors class MyPrintWriter extends PrintWriter { } def pw1 = new MyPrintWriter(new File('out1.txt')) def pw2 = new MyPrintWriter('out2.txt', 'US-ASCII') [pw1, pw2].each { it << 'foo' it.close() } assert new File('out1.txt').text == new File('out2.txt').text ['out1.txt', 'out2.txt'].each{ new File(it).delete() }
  • 42. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 42 @Sortable… • Reduced boiler-plate for Comparable classes including specialised Comparators @Sortable(includes = 'last,initial') class Politician { String first Character initial String last String initials() { first[0] + initial + last[0] } }
  • 43. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 43 …@Sortable // @Sortable(includes = 'last,initial') class Politician { ... } def politicians = [ new Politician(first: 'Margaret', initial: 'H', last: 'Thatcher'), new Politician(first: 'George', initial: 'W', last: 'Bush') ] politicians.with { assert sort()*.initials() == ['GWB', 'MHT'] def comparator = Politician.comparatorByInitial() assert toSorted(comparator)*.initials() == ['MHT', 'GWB'] }
  • 44. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 44 @Builder • Recent Java idiom is to use "fluent-api" builders • Often not needed for Groovy • But if you need Java integration or want improved IDE support… @Builder class Chemist { String first, last int born } def builder = Chemist.builder() def c = builder.first("Marie").last("Curie").born(1867).build()
  • 45. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 45 @Builder • Supports customizable building strategies DefaultStrategy Creates a nested helper class with methods for each property and a build() method SimpleStrategy Creates chainable setters, i.e. each setter returns the object itself ExternalStrategy For creating a builder for a class you don’t have control over, e.g. from a library or another team in your organization. InitializerStrateg y Like DefaultStrategy but when used with @CompileStatic allows type- safe object creation
  • 46. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 46 @Delegate class Clock { void tellTime() { println '7 am' } } class Alarm { void soundAlarm() { println 'Time to wake up' } } class AlarmClock { @Delegate Clock c = new Clock() @Delegate Alarm a = new Alarm() } new AlarmClock().with { tellTime(); soundAlarm() }
  • 47. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 47 @Delegate class Clock { void tellTime() { println '7 am' } } class Alarm { void soundAlarm() { println 'Time to wake up' } } class AlarmClock { @Delegate Clock c = new Clock() @Delegate Alarm a = new Alarm() } new AlarmClock().with { tellTime(); soundAlarm() } class AlarmClock { private Clock c = new Clock() private Alarm a = new Alarm() void tellTime() { c.tellTime() } void soundAlarm() { a.soundAlarm() } }
  • 48. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 48 @Memoized • For making pure functions more efficient class Calc { def log = [] @Memoized int sum(int a, int b) { log << "$a+$b" a + b } } new Calc().with { assert sum(3, 4) == 7 assert sum(4, 4) == 8 assert sum(3, 4) == 7 assert log.join(' ') == '3+4 4+4' }
  • 49. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 49 @TailRecursive • For unravelling recursion import groovy.transform.TailRecursive class RecursiveCalc { @TailRecursive int accumulate(int n, int sum = 0) { (n == 0) ? sum : accumulate(n - 1, sum + n) } } new RecursiveCalc().with { assert accumulate(10) == 55 }
  • 50. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 50 @TailRecursiveclass RecursiveCalc { int accumulate(int n, int sum) { int _sum_ = sum int _n_ = n while (true) { if (_n_ == 0) { return _sum_ } else { int __n__ = _n_ int __sum__ = _sum_ _n_ = __n__ - 1 _sum_ = __sum__ + __n__ } } } int accumulate(int n) { accumulate(n, 0) } }
  • 51. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 51 @Immutable • For unchanging data structures @Immutable class Genius { String firstName, lastName } def g1 = new Genius(firstName: 'Albert', lastName: "Einstein") assert g1.toString() == 'Genius(Albert, Einstein)' def g2 = new Genius('Leonardo', "da Vinci") assert g2.firstName == 'Leonardo' shouldFail(ReadOnlyPropertyException) { g2.lastName = 'DiCaprio' }
  • 52. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 52 @Log @Log4j @Log4j2 @Commons @Slf4j • For easy logging @groovy.util.logging.Log class Database { def search() { log.fine(runLongDatabaseQuery()) } def runLongDatabaseQuery() { println 'Calling database' /* ... */ return 'query result' } } new Database().search()
  • 53. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 53 @Synchronized • For safe synchronization class PhoneBook1 { private final phoneNumbers = [:] @Synchronized def getNumber(key) { phoneNumbers[key] } @Synchronized void addNumber(key, value) { phoneNumbers[key] = value } }
  • 54. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 54 @WithReadLock @WithWriteLock • Declarative and efficient synchronization class PhoneBook2 { private final phoneNumbers = [:] @WithReadLock def getNumber(key) { phoneNumbers[key] } @WithWriteLock def addNumber(key, value) { phoneNumbers[key] = value } }
  • 55. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 55 @AutoClone (Simple example) • Easier cloning. With multiple styles supported: because one size doesn't fit all for cloning on the JVM @AutoClone class Chef1 { String name List<String> recipes Date born } def name = 'Heston Blumenthal' def recipes = ['Snail porridge', 'Bacon & egg ice cream'] def born = Date.parse('yyyy-MM-dd', '1966-05-27') def c1 = new Chef1(name: name, recipes: recipes, born: born) def c2 = c1.clone() assert c2.recipes == recipes
  • 56. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 56 @AutoClone (Advanced example) @TupleConstructor @AutoClone(style=COPY_CONSTRUCTOR) class Person { final String name final Date born } @TupleConstructor(includeSuperProperties=true, callSuper=true) @AutoClone(style=COPY_CONSTRUCTOR) class Chef2 extends Person { final List<String> recipes } def name = 'Jamie Oliver' def recipes = ['Lentil Soup', 'Crispy Duck'] def born = Date.parse('yyyy-MM-dd', '1975-05-27') def c1 = new Chef2(name, born, recipes) def c2 = c1.clone() assert c2.name == name && c2.born == born && c2.recipes == recipes
  • 57. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 57 @AutoCloneStyle Style Description CLONE Adds a clone() method to your class. The clone() method will call super.clone() before calling clone() on each Cloneable property of the class. Doesn’t provide deep cloning. Not suitable if you have final properties. (Default) SIMPLE Adds a clone() method to your class which calls the no-arg constructor then copies each property calling clone() for each Cloneable property. Handles inheritance hierarchies. Not suitable if you have final properties. Doesn’t provide deep cloning. COPY_CONSTRUCTOR Adds a “copy” constructor, i.e. one which takes your class as its parameter, and a clone() method to your class. The copy constructor method copies each property calling clone() for each Cloneable property. The clone() method creates a new instance making use of the copy constructor. Suitable if you have final properties. Handles inheritance hierarchies. Doesn’t provide deep cloning. SERIALIZATION Adds a clone() method to your class which uses serialization to copy your class. Suitable if your class already implements the Serializable or Externalizable interface. Automatically performs deep cloning. Not as time or memory efficient. Not suitable if you have final properties.
  • 58. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 58 @AutoExternalize @AutoExternalize @ToString class Composer { String name int born boolean married } def c = new Composer(name: 'Wolfgang Amadeus Mozart', born: 1756, married: true) def baos = new ByteArrayOutputStream() baos.withObjectOutputStream{ os -> os.writeObject(c) } def bais = new ByteArrayInputStream(baos.toByteArray()) def loader = getClass().classLoader def result bais.withObjectInputStream(loader) { result = it.readObject().toString() } assert result == 'Composer(Wolfgang Amadeus Mozart, 1756, true)'
  • 59. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 59 @TimedInterrupt @ThreadInterrupt @ConditionalInterrupt • For safer scripting • Typically applied through compilation customizers to user scripts rather than directly used
  • 60. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Contents 60 • Introduction • Built-in AST Transforms  Writing your own transforms
  • 61. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 61 Parsing Deep Dive • Purpose Read source files/streams and configure compiler • Key classes CompilerConfiguration CompilationUnit Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization
  • 62. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 62 • Purpose Use (ANTLR) grammar to convert source code into token tree • Key classes CompilationUnit GroovyLexer GroovyRecognizer GroovyTokenTypes • CST Transforms – http://java.dzone.com/articles/groovy- antlr-plugins-better Parsing Deep DiveInitialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization
  • 63. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 63 Parsing Deep Dive • Purpose Token tree converted into abstract syntax tree (AST) and is the first place where we can begin to write AST visitors • Key classes AntlrParserPlugin EnumVisitor • AST Transforms @Grab (global) Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization
  • 64. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 64 Parsing Deep Dive • Purpose Resolves classes and performs consistency and validity checks beyond what the grammar can provide • Key classes StaticVerifier ResolveVisitor StaticImportVisitor InnerClassVisitor, AnnotationCollector • AST Transforms @Lazy @Builder @Field @Log @Memoized @PackageScope @TailRecursive @BaseScript Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization
  • 65. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 65 Parsing Deep Dive • Purpose Finalizes the complete abstract syntax tree and typically the last point at which you want to run a transformation • Key classes InnerClassCompletionVisitor EnumCompletionVisitor, TraitComposer • AST Transforms @Bindable @Vetoable @Mixin @AutoClone @ConditionalInterrupt @ThreadInterrupt @TimedInterrupt @ListenerList @Canonical @Category @Delegate @Bindable @Vetoable @EqualsAndHashCode @AutoExternalize @Immutable @IndexedProperty @Synchronized @InheritConstructors @Sortable @WithReadLock @WithWriteLock @Singleton @Newify @ToString @TupleConstructor Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization
  • 66. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 66 • Purpose Chooses an instruction set for the generated bytecode, e.g. Java 5 versus pre-Java 5 • AST Transforms @CompileStatic @TypeChecked Parsing Deep DiveInitialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization
  • 67. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 67 Parsing Deep Dive • Purpose Creates bytecode based Class in memory • Key classes OptimizerVisitor GenericsVisitor Verifier LabelVerifier ExtendedVerifier ClassCompletionVerifier AsmClassGenerator Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization
  • 68. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 68 Parsing Deep Dive • Purpose Binary output (.class file) written to file system Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization
  • 69. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 69 Parsing Deep Dive • Purpose Used to cleanup any resources no longer needed Initialization Semantic Analysis Instruction Selection Parsing Conversion Canonicalization Class Generation Output Finalization
  • 70. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Visitor Pattern… • separates the object being walked (the tree) from the behavior of the walker (the visitor) 70
  • 71. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ …Visitor Pattern • Consider extending ClassCodeVisitorSupport but also consider extending AbstractASTTransformation or using ClassCodeExpressionTransformer 71
  • 72. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Writing a Local AST Transform… • Create an Annotation • Annotated with @GroovyASTTransformationClass 72 class MainTransformation{} import org.codehaus.groovy.transform.GroovyASTTransformationClass import java.lang.annotation.* @Retention(RetentionPolicy.SOURCE) @Target([ElementType.METHOD]) @GroovyASTTransformationClass(classes = [MainTransformation]) public @interface Main {}
  • 73. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ …Writing a Local AST Transform… • Write your transform class 73 @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION) class MainTransformation implements ASTTransformation { private static final ClassNode[] NO_EXCEPTIONS = ClassNode.EMPTY_ARRAY private static final ClassNode STRING_ARRAY = ClassHelper.STRING_TYPE.makeArray() void visit(ASTNode[] astNodes, SourceUnit sourceUnit) { // use guard clauses as a form of defensive programming if (!astNodes) return if (!astNodes[0] || !astNodes[1]) return if (!(astNodes[0] instanceof AnnotationNode)) return if (astNodes[0].classNode?.name != Main.class.name) return if (!(astNodes[1] instanceof MethodNode)) return ...
  • 74. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ …Writing a Local AST Transform… 74 ... MethodNode annotatedMethod = astNodes[1] ClassNode declaringClass = annotatedMethod.declaringClass def callMethod = callX(ctorX(declaringClass), annotatedMethod.name) Statement body = block(stmt(callMethod)) def visibility = ACC_STATIC | ACC_PUBLIC def parameters = params(param(STRING_ARRAY, 'args')) declaringClass.addMethod('main', visibility, VOID_TYPE, parameters, NO_EXCEPTIONS, body) } }
  • 75. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ …Writing a Local AST Transform • Use the transform 75 class Greeter { @Main def greet() { println "Hello from the greet() method!" } }
  • 76. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ …Writing a Local AST Transform • Use the transform 76 class Greeter { @Main def greet() { println "Hello from the greet() method!" } } new GroovyShell(getClass().classLoader).evaluate ''' class Greeter { @Main def greet() { println "Hello from the greet() method!" } } '''
  • 77. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Creating AST… • By hand (raw) • Verbose, full IDE supported 77 import org.codehaus.groovy.ast.* import org.codehaus.groovy.ast.stmt.* import org.codehaus.groovy.ast.expr.* new ReturnStatement( new ConstructorCallExpression( ClassHelper.make(Date), ArgumentListExpression.EMPTY_ARGUMENTS ) )
  • 78. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ …Creating AST… • By hand (with helper utility methods) • Concise, not everything has concise form (yet) 78 import static org.codehaus.groovy.ast.tools.GeneralUtils.* import static org.codehaus.groovy.ast.ClassHelper.* returnS(ctorX(make(Date)))
  • 79. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ …Creating AST… • With ASTBuilder (from a specification/DSL) • Requires AST knowledge, limited IDE support 79 import org.codehaus.groovy.ast.builder.AstBuilder def ast = new AstBuilder().buildFromSpec { returnStatement { constructorCall(Date) { argumentList {} } } }
  • 80. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ …Creating AST… • With ASTBuilder (from a String) • Concise, intuitive, can't create everything, limited IDE support 80 import org.codehaus.groovy.ast.builder.AstBuilder def ast = new AstBuilder().buildFromString('new Date()')
  • 81. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ …Creating AST… • With ASTBuilder (from code) • Clear, concise, some entities cannot be created, IDE assistance 81 import org.codehaus.groovy.ast.builder.AstBuilder def ast = new AstBuilder().buildFromCode { new Date() }
  • 82. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ …Creating AST • ASTBuilder limitations • Great for prototyping, not always suitable for production transforms • Groovy technology, can be slow, subject to global transforms • Sometimes wasteful, e.g. you might need to create more than you need such as creating a whole class to then pull out one method • Some flavors don't support arbitrary node types, don't make it easy to handle interactions with existing class nodes, don't make it easy to support redirects or generics, nor allow you to properly set the line/column numbers resulting in difficult to debug AST transform and cryptic compilation errors 82
  • 83. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Feature Interactions • Consider Groovy's @ToString annotation transform which runs at the end of the Canonicalization phase • Now suppose we want to create a @Trace annotation transform which when placed on a class will "instrument" each method with "trace" println statements, e.g. this: becomes: • What behaviour should I expect calling toString() if @Trace runs at the end of Semantic Analysis? Canonicalization? Instruction Selection? 83 def setX(x) { this.x = x } def setX(x) { println "setX begin" this.x = x println "setX end" }
  • 84. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Testing AST Transforms • Consider using ASTTest • Test both the AST tree and the end-to-end behavior • Consider writing defensive guards • Use GroovyConsole 84
  • 85. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Design Considerations • Don't reuse ClassNodes • Compile-time vs Runtime trade-offs and typing • Feature Interactions/Fragility • Beware Complexity • Risks of introducing bugs • Avoid global transforms (unless needed) • GroovyConsole is your friend (AST/bytecode) • Use addError for errors • Retain line/column number information when transforming • Watch variable scoping 85
  • 86. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Further Information • Documentation • http://docs.groovy-lang.org/docs/groovy- latest/html/documentation/#_compile_time_metaprogramming • Macro Groovy • https://github.com/bsideup/MacroGroovy • https://github.com/bsideup/groovy-macro-methods • AST Workshop • http://melix.github.io/ast-workshop/ 86
  • 87. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ More Information: Groovy in Action, 2ed 87 Manning promo code: ctwspringo2gx
  • 88. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Bonus Material
  • 89. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 89 Groovy and "fluent-api" builders • A common idiom in recent times for Java is to use an inner helper class and accompanying "fluent-api" to reduce ceremony when creating Java classes with many parameters – But Groovy's named parameters greatly reduces this need class Chemist { String first String last int born } def c = new Chemist(first: "Marie", last: "Curie", born: 1867) assert c.first == "Marie" assert c.last == "Curie" assert c.born == 1867
  • 90. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 90 @Builder… • But if you need Java integration or want improved IDE support… import groovy.transform.builder.Builder @Builder class Chemist { String first String last int born } def builder = Chemist.builder() def c = builder.first("Marie").last("Curie").born(1867).build() assert c.first == "Marie" assert c.last == "Curie" assert c.born == 1867
  • 91. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 91 …@Builder… • And it supports customizable building strategies Strategy Description DefaultStrategy Creates a nested helper class for instance creation. Each method in the helper class returns the helper until finally a build() method is called which returns a created instance. SimpleStrategy Creates chainable setters, i.e. each setter returns the object itself after updating the appropriate property. ExternalStrategy Allows you to annotate an explicit builder class while leaving some buildee class being built untouched. This is appropriate when you want to create a builder for a class you don’t have control over, e.g. from a library or another team in your organization. InitializerStrategyCreates a nested helper class for instance creation which when used with @CompileStatic allows type-safe object creation.
  • 92. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 92 …@Builder • Type-safe construction using phantom types (*if* you need it) @Builder(builderStrategy=InitializerStrategy) @Immutable class Chemist { String first, last int born } @CompileStatic def solution() { def init = Chemist.createInitializer().first("Marie").last("Curie").born(1867) new Chemist(init).with { assert first == "Marie" assert last == "Curie" assert born == 1867 } } solution()
  • 93. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 93 @Delegate (motivation) • Anything wrong with this? class NoisySet extends HashSet { @Override boolean add(item) { println "adding $item" super.add(item) } @Override boolean addAll(Collection items) { items.each { println "adding $it" } super.addAll(items) } }
  • 94. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 94 @Delegate (motivation) • Anything wrong with this? • Could we fix this implementation? class NoisySet extends HashSet { @Override boolean add(item) { println "adding $item" super.add(item) } @Override boolean addAll(Collection items) { items.each { println "adding $it" } super.addAll(items) } }
  • 95. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 95 @Delegate (motivation) • Anything wrong with this? • Could we fix this implementation? • What about using the delegate pattern written by hand? class NoisySet extends HashSet { @Override boolean add(item) { println "adding $item" super.add(item) } @Override boolean addAll(Collection items) { items.each { println "adding $it" } super.addAll(items) } }
  • 96. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 96 @Delegate • For declarative but flexible use of the delegate pattern class NoisySet { @Delegate Set delegate = new HashSet() @Override boolean add(item) { println "adding $item" delegate.add(item) } @Override boolean addAll(Collection items) { items.each { println "adding $it" } delegate.addAll(items) } } Set ns = new NoisySet() ns.add(1) ns.addAll([2, 3]) assert ns.size() == 3
  • 97. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 97 @Delegate annotation parameters… Parameter Name Purpose interfaces Set this parameter to true to make the owner class implement the same interfaces as the delegate, which is the default behavior. To make the owner not implement the delegate interfaces, set this parameter to false. deprecated Set this parameter to true to have the owner class delegate methods marked as @Deprecated in the delegate. By default @Deprecated methods are not delegated. methodAnnotations Set to true if you want to carry over annotations from the methods of the delegate to your delegating method. By default, annotations are not carried over. Currently Closure annotation members are not supported. parameterAnnotations Set to true if you want to carry over annotations from the method parameters of the delegate to your delegating method. By default, annotations are not carried over. Currently Closure annotation members are not supported.
  • 98. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 98 …@Delegate annotation parameters Only one of 'includes', 'includeTypes', 'excludes' or 'excludeTypes' should be used Parameter Name Purpose excludes List of method and/or property names to exclude when delegating. excludeTypes List of interfaces containing method signatures to exclude when delegating. includes List of method and/or property names to include when delegating. includeTypes List of interfaces containing method signatures to exclude when delegating.
  • 99. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 99 @Memoized • For making pure functions more efficient class Calc { def log = [] @Memoized int sum(int a, int b) { log << "$a+$b" a + b } } new Calc().with { assert sum(3, 4) == 7 assert sum(4, 4) == 8 assert sum(3, 4) == 7 assert log.join(' ') == '3+4 4+4' }
  • 100. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 100 @TailRecursive • For unravelling recursion import groovy.transform.TailRecursive class RecursiveCalc { @TailRecursive int accumulate(int n, int sum = 0) { (n == 0) ? sum : accumulate(n - 1, sum + n) } } new RecursiveCalc().with { assert accumulate(10) == 55 }
  • 101. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 101 @TailRecursiveclass RecursiveCalc { int accumulate(int n, int sum) { int _sum_ = sum int _n_ = n while (true) { if (_n_ == 0) { return _sum_ } else { int __n__ = _n_ int __sum__ = _sum_ _n_ = __n__ - 1 _sum_ = __sum__ + __n__ } } } int accumulate(int n) { accumulate(n, 0) } }
  • 102. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 102 @Immutable • For unchanging data structures @Immutable class Genius { String firstName, lastName } def g1 = new Genius(firstName: 'Albert', lastName: "Einstein") assert g1.toString() == 'Genius(Albert, Einstein)' def g2 = new Genius('Leonardo', "da Vinci") assert g2.firstName == 'Leonardo' shouldFail(ReadOnlyPropertyException) { g2.lastName = 'DiCaprio' }
  • 103. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 103 @Log @Log4j @Log4j2 @Commons @Slf4j • For easy logging @groovy.util.logging.Log class Database { def search() { log.fine(runLongDatabaseQuery()) } def runLongDatabaseQuery() { println 'Calling database' /* ... */ return 'query result' } } new Database().search()
  • 104. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 104 @Synchronized • For safe synchronization class PhoneBook1 { private final phoneNumbers = [:] @Synchronized def getNumber(key) { phoneNumbers[key] } @Synchronized void addNumber(key, value) { phoneNumbers[key] = value } }
  • 105. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 105 @WithReadLock @WithWriteLock • Declarative and efficient synchronization class PhoneBook2 { private final phoneNumbers = [:] @WithReadLock def getNumber(key) { phoneNumbers[key] } @WithWriteLock def addNumber(key, value) { phoneNumbers[key] = value } }
  • 106. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 106 @AutoClone (Simple example) • Easier cloning. With multiple styles supported: because one size doesn't fit all for cloning on the JVM @AutoClone class Chef1 { String name List<String> recipes Date born } def name = 'Heston Blumenthal' def recipes = ['Snail porridge', 'Bacon & egg ice cream'] def born = Date.parse('yyyy-MM-dd', '1966-05-27') def c1 = new Chef1(name: name, recipes: recipes, born: born) def c2 = c1.clone() assert c2.recipes == recipes
  • 107. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 107 @AutoClone (Advanced example) @TupleConstructor @AutoClone(style=COPY_CONSTRUCTOR) class Person { final String name final Date born } @TupleConstructor(includeSuperProperties=true, callSuper=true) @AutoClone(style=COPY_CONSTRUCTOR) class Chef2 extends Person { final List<String> recipes } def name = 'Jamie Oliver' def recipes = ['Lentil Soup', 'Crispy Duck'] def born = Date.parse('yyyy-MM-dd', '1975-05-27') def c1 = new Chef2(name, born, recipes) def c2 = c1.clone() assert c2.name == name && c2.born == born && c2.recipes == recipes
  • 108. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 108 @AutoCloneStyle Style Description CLONE Adds a clone() method to your class. The clone() method will call super.clone() before calling clone() on each Cloneable property of the class. Doesn’t provide deep cloning. Not suitable if you have final properties. (Default) SIMPLE Adds a clone() method to your class which calls the no-arg constructor then copies each property calling clone() for each Cloneable property. Handles inheritance hierarchies. Not suitable if you have final properties. Doesn’t provide deep cloning. COPY_CONSTRUCTOR Adds a “copy” constructor, i.e. one which takes your class as its parameter, and a clone() method to your class. The copy constructor method copies each property calling clone() for each Cloneable property. The clone() method creates a new instance making use of the copy constructor. Suitable if you have final properties. Handles inheritance hierarchies. Doesn’t provide deep cloning. SERIALIZATION Adds a clone() method to your class which uses serialization to copy your class. Suitable if your class already implements the Serializable or Externalizable interface. Automatically performs deep cloning. Not as time or memory efficient. Not suitable if you have final properties.
  • 109. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 109 @AutoExternalize @AutoExternalize @ToString class Composer { String name int born boolean married } def c = new Composer(name: 'Wolfgang Amadeus Mozart', born: 1756, married: true) def baos = new ByteArrayOutputStream() baos.withObjectOutputStream{ os -> os.writeObject(c) } def bais = new ByteArrayInputStream(baos.toByteArray()) def loader = getClass().classLoader def result bais.withObjectInputStream(loader) { result = it.readObject().toString() } assert result == 'Composer(Wolfgang Amadeus Mozart, 1756, true)'
  • 110. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 110 @TimedInterrupt @ThreadInterrupt @ConditionalInterrupt • For safer scripting • Typically applied through compilation customizers to user scripts rather than directly used
  • 111. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 111 @TimedInterrupt @TimedInterrupt(value = 520L, unit = MILLISECONDS) class BlastOff1 { def log = [] def countdown(n) { sleep 100 log << n if (n == 0) log << 'ignition' else countdown(n - 1) } } def b = new BlastOff1() Thread.start { try { b.countdown(10) } catch (TimeoutException ignore) { b.log << 'aborted' } }.join() assert b.log.join(' ') == '10 9 8 7 6 aborted'
  • 112. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 112 @ThreadInterrupt @ThreadInterrupt class BlastOff2 { def log = [] def countdown(n) { Thread.sleep 100 log << n if (n == 0) log << 'ignition' else countdown(n - 1) } } def b = new BlastOff2() def t1 = Thread.start { try { b.countdown(10) } catch(InterruptedException ignore) { b.log << 'aborted' } } sleep 570 t1.interrupt() t1.join() assert b.log.join(' ') == '10 9 8 7 6 aborted'
  • 113. Unless otherwise indicated, these slides are © 2013 -2015 ASERT and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ 113 @ConditionalInterrupt @ConditionalInterrupt({ count <= 5 }) class BlastOff3 { def log = [] def count = 10 def countdown() { while (count != 0) { log << count count-- } log << 'ignition' } } def b = new BlastOff3() try { b.countdown() } catch (InterruptedException ignore) { b.log << 'aborted' } assert b.log.join(' ') == '10 9 8 7 6 aborted'