Closure’s own memoization support !8 import groovy.transform.Memoized ! @Memoized int expensiveOp(int a, int b) { sleep 1000 return a + b } ! expensiveOp(1, 2) ! expensiveOp(1, 2)
Closure’s own memoization support !8 import groovy.transform.Memoized ! @Memoized int expensiveOp(int a, int b) { sleep 1000 return a + b } ! expensiveOp(1, 2) ! expensiveOp(1, 2) First call takes 1 s
Closure’s own memoization support !8 import groovy.transform.Memoized ! @Memoized int expensiveOp(int a, int b) { sleep 1000 return a + b } ! expensiveOp(1, 2) ! expensiveOp(1, 2) First call takes 1 s Subsequent calls return immediately from the cache
! @Log4j2 class MyClass { void doSomething() { log.info "did something groovy today!" } } Just like our other logger implementations, but for Log4j2 Log, log, log away…
import groovy.transform.BaseScript ! abstract class DeclaredBaseScript extends Script { int meaningOfLife = 42 } @BaseScript DeclaredBaseScript baseScript ! assert meaningOfLife == 42 Declare a variable annotated with @BaseScript, whose type is a subclass of Script Your own Script class extension
Special base script class to delegate method calls and property accesses to a custom object !26 name = "Guillaume" sayHi() class Person { String name ! void sayHi() { println "Hi $name" } }
Special base script class to delegate method calls and property accesses to a custom object !26 name = "Guillaume" sayHi() class Person { String name ! void sayHi() { println "Hi $name" } } Your script
Special base script class to delegate method calls and property accesses to a custom object !26 name = "Guillaume" sayHi() class Person { String name ! void sayHi() { println "Hi $name" } } Your script Delegate property access and method call to this custom class
Special base script class to delegate method calls and property accesses to a custom object !26 name = "Guillaume" sayHi() class Person { String name ! void sayHi() { println "Hi $name" } } Your script Delegate property access and method call to this custom class How do I do that?
def cc = new CompilerConfiguration() cc.scriptBaseClass = DelegatingScript.class.name ! def sh = new GroovyShell(cc) def script = sh.parse(file) ! def p = new Person() script.setDelegate(p) script.run() ! assert p.name == "Guillaume" Specify DelegatingScript base class
def cc = new CompilerConfiguration() cc.scriptBaseClass = DelegatingScript.class.name ! def sh = new GroovyShell(cc) def script = sh.parse(file) ! def p = new Person() script.setDelegate(p) script.run() ! assert p.name == "Guillaume" Specify DelegatingScript base class Parse the script
def cc = new CompilerConfiguration() cc.scriptBaseClass = DelegatingScript.class.name ! def sh = new GroovyShell(cc) def script = sh.parse(file) ! def p = new Person() script.setDelegate(p) script.run() ! assert p.name == "Guillaume" Specify DelegatingScript base class Parse the script Define the delegate
def cc = new CompilerConfiguration() cc.scriptBaseClass = DelegatingScript.class.name ! def sh = new GroovyShell(cc) def script = sh.parse(file) ! def p = new Person() script.setDelegate(p) script.run() ! assert p.name == "Guillaume" Specify DelegatingScript base class Parse the script Define the delegate Run the script
def cc = new CompilerConfiguration() cc.scriptBaseClass = DelegatingScript.class.name ! def sh = new GroovyShell(cc) def script = sh.parse(file) ! def p = new Person() script.setDelegate(p) script.run() ! assert p.name == "Guillaume" Specify DelegatingScript base class Parse the script Define the delegate Run the script Be Happy!
@DelegatesTo help the type checker & IDEs provide better support for DSL using closure delegation !28 @groovy.transform.InheritConstructors class MyList extends LinkedList<String> {} ! public <T> Object map( @DelegatesTo.Target List<T> target, @DelegatesTo(genericTypeIndex = 0) Closure arg) { arg.delegate = target.join('') arg() } ! def test() { map(new MyList(['f', 'o', 'o'])) { assert toUpperCase() == 'FOO' } } We want to delegate to the token type T
@DelegatesTo help the type checker & IDEs provide better support for DSL using closure delegation !28 @groovy.transform.InheritConstructors class MyList extends LinkedList<String> {} ! public <T> Object map( @DelegatesTo.Target List<T> target, @DelegatesTo(genericTypeIndex = 0) Closure arg) { arg.delegate = target.join('') arg() } ! def test() { map(new MyList(['f', 'o', 'o'])) { assert toUpperCase() == 'FOO' } } We want to delegate to the token type T We define the index of the type token
@DelegatesTo help the type checker & IDEs provide better support for DSL using closure delegation !28 @groovy.transform.InheritConstructors class MyList extends LinkedList<String> {} ! public <T> Object map( @DelegatesTo.Target List<T> target, @DelegatesTo(genericTypeIndex = 0) Closure arg) { arg.delegate = target.join('') arg() } ! def test() { map(new MyList(['f', 'o', 'o'])) { assert toUpperCase() == 'FOO' } } We want to delegate to the token type T We define the index of the type token This is String’s toUpperCase() method
Custom type checking extensions can work with compiled extensions as well !30 @TypeChecked(extensions = 'com.enterprise.MyDslExtension') void myBusinessMethod() { // ... }
Custom type checking extensions can work with compiled extensions as well !30 @TypeChecked(extensions = 'com.enterprise.MyDslExtension') void myBusinessMethod() { // ... } Fully qualified name of the extension class
Custom type checking extensions can work with compiled extensions as well !30 @TypeChecked(extensions = 'com.enterprise.MyDslExtension') void myBusinessMethod() { // ... } Fully qualified name of the extension class Here, your statically type checked DSL code!
!31 incompatibleReturnType { returnStmt, inferredReturnType -‐> if (inferredReturnType == STRING_TYPE) { handled = true } } incompatibleReturnType event The referred return type is incompatible with the method’s declared return type
!31 incompatibleReturnType { returnStmt, inferredReturnType -‐> if (inferredReturnType == STRING_TYPE) { handled = true } } incompatibleReturnType event The referred return type is incompatible with the method’s declared return type Assume that if the inferred return type is String, then it’s correct (the DSL handles it)
!32 ambiguousMethods { methods, origin -‐> methods.find { it.parameters.any { it.type == classNodeFor(Integer) } } } ambiguousMethods event If one of the methods takes an Integer argument, pick this one
groovy.transform.Immutable ! @Immutable(copyWith = true) class User { String name String email } ! def jdEx = new User('John Doe', '[email protected]') ! def jdGmail = jdEx.copyWith(email: '[email protected]') ! jdGmail.with { assert name == 'John Doe' assert email == '[email protected]' } Specify you want the copyWith() method New immutable user with a different email
JCenter first in the resolver chain for your @Grab statements ! – faster and more responsive than Maven Central – always proper dependencies with checksums – stores and caches dependencies coming from Maven Central if not initially available !43
New parsers and builders have been contributed – by Rick Hightower & Andrey Bloschetsov ! • Making Groovy serialization / deserialization faster than – GSON – Jackson !45
!46 import groovy.text.markup.* ! MarkupTemplateEngine engine = new MarkupTemplateEngine( new TemplateConfiguration()) def template = engine.createTemplate ''' html { head { include template: 'header.gtpl' } body { p "Hello Greach" } } ''' System.out << template.make()
String fly() { "I believe I can fly!" } } A trait keyword applying the @Trait transformation class Car implements FlyingAbility {} A class « implements » the trait def c = new Car() assert c.fly() == "I believe I can fly!"
stateful – Java 8’s default methods won’t cut it here ! • Precedence: When implementing several traits, first declared trait wins – in case both traits have a method with the same signature ! • Supports runtime mixing of traits with ‘as’ !48
String name } ! trait NameSpeakable implements Named { String speak() { "My name is ${name}" } } ! class Phone implements NamedSpeakable {} ! def phone = new Phone(name: 'Galaxy S3') assert phone.speak() == 'My name is Galaxy S3'
String name } ! trait NameSpeakable implements Named { String speak() { "My name is ${name}" } } ! class Phone implements NamedSpeakable {} ! def phone = new Phone(name: 'Galaxy S3') assert phone.speak() == 'My name is Galaxy S3' Implement the parent trait
• Fully leverage and build upon JDK 7+ invoke dynamic – get Java-like performance even for dynamic code • Rationalize the sedimentation of meta-programming – more coherence, less corner cases & inconsistencies • Provide a notion of « realm » – shield users of « monkey patching » – finer-grained control of meta-programming reach • Private visibility anyone? !60
Groovy still uses Antlr v2! •but version 3 and 4 are out – Groovy’s grammar evolved from a Java grammar •harder to fix and evolve, especially with Antlr v2 • Advantages – Easier to start from a clean slate – Antlr 4 more tolerant and powerful regarding ambiguities – Time to clean some grammar & syntax warts! – Need to implement the Java 8 constructs! !62
Groovy still uses Antlr v2! •but version 3 and 4 are out – Groovy’s grammar evolved from a Java grammar •harder to fix and evolve, especially with Antlr v2 • Advantages – Easier to start from a clean slate – Antlr 4 more tolerant and powerful regarding ambiguities – Time to clean some grammar & syntax warts! – Need to implement the Java 8 constructs! !62
and semantics features to support – to keep saying Groovy / Java interop is awesome! ! • New in Java 8 – lambdas – method references – default methods in interfaces – stream API, date / time API – annotations on types & repeated annotations !64