Piggypack on 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)
Piggypack on 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
Piggypack on 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
import groovy.util.logging.Log4j2 ! @Log4j2 class MyClass { void doSomething() { log.info "did something groovy today!" } } Just like our other logger implementations, but for Log4j2
import groovy.util.logging.Log4j2 ! @Log4j2 class MyClass { void doSomething() { log.info "did something groovy today!" } } Just like our other logger implementations, but for Log4j2 Log, log, log away…
definition !25 import groovy.transform.BaseScript ! abstract class DeclaredBaseScript extends Script { int meaningOfLife = 42 } Your own Script class extension
definition !25 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
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" } }
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
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
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?
class !27 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"
class !27 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
class !27 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
class !27 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
class !27 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
class !27 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!
tokens • @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
tokens • @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
tokens • @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
precompilation • Custom type checking extensions can work with compiled extensions as well !30 @TypeChecked(extensions = 'com.enterprise.MyDslExtension') void myBusinessMethod() { // ... }
precompilation • 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
precompilation • 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!
new events !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
new events !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)
new events !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
!37 import 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
JFrog’s Bintray 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
{ 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!"
questions remain: ! – Should traits be stateful or stateless? ! – Aren’t Java 8 interface default methods enough for traits? ! – What are the rules of precedence / shadowing when implementing 2 traits with methods of the same signature? ! – What is the best implementation approach? (delegation based, bytecode weaving, companion-class…) !46
questions remain: ! – Should traits be stateful or stateless? ! – Aren’t Java 8 interface default methods enough for traits? ! – What are the rules of precedence / shadowing when implementing 2 traits with methods of the same signature? ! – What is the best implementation approach? (delegation based, bytecode weaving, companion-class…) !46
questions remain: ! – Should traits be stateful or stateless? ! – Aren’t Java 8 interface default methods enough for traits? ! – What are the rules of precedence / shadowing when implementing 2 traits with methods of the same signature? ! – What is the best implementation approach? (delegation based, bytecode weaving, companion-class…) !46 0QVUVCVGHWN
questions remain: ! – Should traits be stateful or stateless? ! – Aren’t Java 8 interface default methods enough for traits? ! – What are the rules of precedence / shadowing when implementing 2 traits with methods of the same signature? ! – What is the best implementation approach? (delegation based, bytecode weaving, companion-class…) !46 0QVUVCVGHWN (KTUVYKPU
questions remain: ! – Should traits be stateful or stateless? ! – Aren’t Java 8 interface default methods enough for traits? ! – What are the rules of precedence / shadowing when implementing 2 traits with methods of the same signature? ! – What is the best implementation approach? (delegation based, bytecode weaving, companion-class…) !46 0QVUVCVGHWN (KTUVYKPU
Meta-Object Protocol • 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? !54
Problems – 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! !56
Additional grammar 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 !58