Share
Embed
Groovy in 2014 and beyond
Talk given at the Greach conference in Madrid, in 2014.
Covers features of Groovy 2.2 and 2.3, and talks about Groovy 3's roadmap.
Guillaume Laforge
March 28, 2014
Transcript
149 slides
-
Guillaume Laforge Groovy project lead at . ! @glaforge
http://glaforge.appspot.com -
@glaforge — Greach 2014 Implicit closure coercion !6 interface Predicate
{ boolean test(something) } ! List filter(List l, Predicate p) { l.findAll { p.test(it) } } ! filter([1, 2, 3, 4], { it > 2 } as Predicate) ! filter([1, 2, 3, 4], { it > 2 }) ! filter([1, 2, 3, 4]) { it > 2 } -
@glaforge — Greach 2014 Implicit closure coercion !6 interface Predicate
{ boolean test(something) } ! List filter(List l, Predicate p) { l.findAll { p.test(it) } } ! filter([1, 2, 3, 4], { it > 2 } as Predicate) ! filter([1, 2, 3, 4], { it > 2 }) ! filter([1, 2, 3, 4]) { it > 2 } « Functional » interface -
@glaforge — Greach 2014 Implicit closure coercion !6 interface Predicate
{ boolean test(something) } ! List filter(List l, Predicate p) { l.findAll { p.test(it) } } ! filter([1, 2, 3, 4], { it > 2 } as Predicate) ! filter([1, 2, 3, 4], { it > 2 }) ! filter([1, 2, 3, 4]) { it > 2 } « Functional » interface A bit verbose? -
@glaforge — Greach 2014 Implicit closure coercion !6 interface Predicate
{ boolean test(something) } ! List filter(List l, Predicate p) { l.findAll { p.test(it) } } ! filter([1, 2, 3, 4], { it > 2 } as Predicate) ! filter([1, 2, 3, 4], { it > 2 }) ! filter([1, 2, 3, 4]) { it > 2 } « Functional » interface A bit verbose? Implicit closure coercion -
@glaforge — Greach 2014 Implicit closure coercion !6 interface Predicate
{ boolean test(something) } ! List filter(List l, Predicate p) { l.findAll { p.test(it) } } ! filter([1, 2, 3, 4], { it > 2 } as Predicate) ! filter([1, 2, 3, 4], { it > 2 }) ! filter([1, 2, 3, 4]) { it > 2 } (it) -‐> it > 2 « Functional » interface A bit verbose? Implicit closure coercion -
@glaforge — Greach 2014 Implicit closure coercion !6 interface Predicate
{ boolean test(something) } ! List filter(List l, Predicate p) { l.findAll { p.test(it) } } ! filter([1, 2, 3, 4], { it > 2 } as Predicate) ! filter([1, 2, 3, 4], { it > 2 }) ! filter([1, 2, 3, 4]) { it > 2 } (it) -‐> it > 2 « Functional » interface Works with single abstract method classes too A bit verbose? Implicit closure coercion -
@glaforge — Greach 2014 @Memoized AST transformation • 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) -
@glaforge — Greach 2014 @Memoized AST transformation • 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 -
@glaforge — Greach 2014 @Memoized AST transformation • 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 -
@glaforge — Greach 2014 @Memoized AST transformation !9 import groovy.transform.Memoized
! @Memoized long fib(long n) { n < 2 ? 1 : fib(n-‐1) + fib(n-‐2) } ! fib(40) -
@glaforge — Greach 2014 @Memoized AST transformation !9 import groovy.transform.Memoized
! @Memoized long fib(long n) { n < 2 ? 1 : fib(n-‐1) + fib(n-‐2) } ! fib(40) Handy to cache recursive call results! -
@glaforge — Greach 2014 @Memoized AST transformation !9 import groovy.transform.Memoized
! @Memoized long fib(long n) { n < 2 ? 1 : fib(n-‐1) + fib(n-‐2) } ! fib(40) Handy to cache recursive call results! Instantaneous result -
@glaforge — Greach 2014 @Memoized AST transformation !10 fib(7) fib(5)
fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) … … … -
@glaforge — Greach 2014 @Memoized AST transformation !11 fib(7) fib(5)
fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) … … … -
@glaforge — Greach 2014 @Memoized AST transformation !12 fib(7) fib(5)
fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) … … … -
@glaforge — Greach 2014 @Memoized AST transformation !13 fib(7) fib(5)
fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) … … … -
@glaforge — Greach 2014 @Memoized AST transformation !14 fib(7) fib(5)
fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) … … … -
@glaforge — Greach 2014 @Memoized AST transformation !15 fib(7) fib(5)
fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) … … … -
@glaforge — Greach 2014 @Memoized AST transformation !16 fib(7) fib(5)
fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) … … … -
@glaforge — Greach 2014 … … … @Memoized AST transformation
!17 fib(7) fib(5) fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) -
@glaforge — Greach 2014 … … … @Memoized AST transformation
!17 fib(7) fib(5) fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) Already calculated, in cache! -
@glaforge — Greach 2014 @Memoized AST transformation !18 fib(7) fib(5)
fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) … … … -
@glaforge — Greach 2014 @Memoized AST transformation !19 fib(7) fib(5)
fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) … … … -
@glaforge — Greach 2014 @Memoized AST transformation !20 fib(7) fib(5)
fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) … … … -
@glaforge — Greach 2014 @Memoized AST transformation !21 fib(7) fib(5)
fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) … … … -
@glaforge — Greach 2014 @Memoized AST transformation !21 fib(7) fib(5)
fib(6) fib(3) fib(4) fib(4) fib(5) fib(1) fib(2) fib(2) fib(3) fib(2) fib(3) fib(3) fib(4) Much less computation, no abysmal call stack … … … -
@glaforge — Greach 2014 Log4j2 @Log variant !22 import groovy.util.logging.Log4j2
! @Log4j2 class MyClass { void doSomething() { log.info "did something groovy today!" } } -
@glaforge — Greach 2014 Log4j2 @Log variant !22 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 -
@glaforge — Greach 2014 Log4j2 @Log variant !22 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… -
@glaforge — Greach 2014 Choose which delegated methods to use
!23 class Event { @Delegate(excludes = ['after']) Date start @Delegate(excludes = ['before']) Date end String description } ! def today = new Date().clearTime() def nextWeek = today + 7 ! def event = new Event(description: 'Groovy conference', start: nextWeek, end: nextWeek + 2) ! assert event.before(nextWeek + 1) assert !event.before(nextWeek -‐ 1) assert !event.before(today) ! assert event.after(today) assert event.after(nextWeek + 1) assert !event.after(nextWeek + 3) -
@glaforge — Greach 2014 Choose which delegated methods to use
!23 class Event { @Delegate(excludes = ['after']) Date start @Delegate(excludes = ['before']) Date end String description } ! def today = new Date().clearTime() def nextWeek = today + 7 ! def event = new Event(description: 'Groovy conference', start: nextWeek, end: nextWeek + 2) ! assert event.before(nextWeek + 1) assert !event.before(nextWeek -‐ 1) assert !event.before(today) ! assert event.after(today) assert event.after(nextWeek + 1) assert !event.after(nextWeek + 3) Exclude certain methods from being delegated -
@glaforge — Greach 2014 Choose which delegated methods to use
!23 class Event { @Delegate(excludes = ['after']) Date start @Delegate(excludes = ['before']) Date end String description } ! def today = new Date().clearTime() def nextWeek = today + 7 ! def event = new Event(description: 'Groovy conference', start: nextWeek, end: nextWeek + 2) ! assert event.before(nextWeek + 1) assert !event.before(nextWeek -‐ 1) assert !event.before(today) ! assert event.after(today) assert event.after(nextWeek + 1) assert !event.after(nextWeek + 3) Exclude certain methods from being delegated Delegate to start date -
@glaforge — Greach 2014 Choose which delegated methods to use
!23 class Event { @Delegate(excludes = ['after']) Date start @Delegate(excludes = ['before']) Date end String description } ! def today = new Date().clearTime() def nextWeek = today + 7 ! def event = new Event(description: 'Groovy conference', start: nextWeek, end: nextWeek + 2) ! assert event.before(nextWeek + 1) assert !event.before(nextWeek -‐ 1) assert !event.before(today) ! assert event.after(today) assert event.after(nextWeek + 1) assert !event.after(nextWeek + 3) Exclude certain methods from being delegated Delegate to start date Delegate to end date -
@glaforge — Greach 2014 Annotation-driven base script class definition !25
import groovy.transform.BaseScript ! abstract class DeclaredBaseScript extends Script { int meaningOfLife = 42 } -
@glaforge — Greach 2014 Annotation-driven base script class definition !25
import groovy.transform.BaseScript ! abstract class DeclaredBaseScript extends Script { int meaningOfLife = 42 } Your own Script class extension -
@glaforge — Greach 2014 Annotation-driven base script class definition !25
import groovy.transform.BaseScript ! abstract class DeclaredBaseScript extends Script { int meaningOfLife = 42 } @BaseScript DeclaredBaseScript baseScript ! assert meaningOfLife == 42 Your own Script class extension -
@glaforge — Greach 2014 Annotation-driven base script class 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 -
@glaforge — Greach 2014 DelegatingScript new base 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" } } -
@glaforge — Greach 2014 DelegatingScript new base 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 -
@glaforge — Greach 2014 DelegatingScript new base 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 -
@glaforge — Greach 2014 DelegatingScript new base 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 How do I do that? -
@glaforge — Greach 2014 DelegatingScript new base 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" -
@glaforge — Greach 2014 DelegatingScript new base 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 -
@glaforge — Greach 2014 DelegatingScript new base 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 -
@glaforge — Greach 2014 DelegatingScript new base 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 -
@glaforge — Greach 2014 DelegatingScript new base 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 -
@glaforge — Greach 2014 DelegatingScript new base 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! -
@glaforge — Greach 2014 @DelegatesTo with generics type 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' } } -
@glaforge — Greach 2014 @DelegatesTo with generics type 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 -
@glaforge — Greach 2014 @DelegatesTo with generics type 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 -
@glaforge — Greach 2014 @DelegatesTo with generics type 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 -
@glaforge — Greach 2014 Type checking extensions — precompilation •
Custom type checking extensions can work with compiled extensions as well !30 @TypeChecked(extensions = 'com.enterprise.MyDslExtension') void myBusinessMethod() { // ... } -
@glaforge — Greach 2014 Type checking extensions — 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 -
@glaforge — Greach 2014 Type checking extensions — 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! -
@glaforge — Greach 2014 Type checking extensions — new events
!31 incompatibleReturnType { returnStmt, inferredReturnType -‐> if (inferredReturnType == STRING_TYPE) { handled = true } } -
@glaforge — Greach 2014 Type checking extensions — new events
!31 incompatibleReturnType { returnStmt, inferredReturnType -‐> if (inferredReturnType == STRING_TYPE) { handled = true } } incompatibleReturnType event -
@glaforge — Greach 2014 Type checking extensions — 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 -
@glaforge — Greach 2014 Type checking extensions — 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) -
@glaforge — Greach 2014 Type checking extensions — new events
!32 ambiguousMethods { methods, origin -‐> methods.find { it.parameters.any { it.type == classNodeFor(Integer) } } } -
@glaforge — Greach 2014 Type checking extensions — new events
!32 ambiguousMethods { methods, origin -‐> methods.find { it.parameters.any { it.type == classNodeFor(Integer) } } } ambiguousMethods event -
@glaforge — Greach 2014 Type checking extensions — 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 -
@glaforge — Greach 2014 Type checking extensions — dynamic calls
• New makeDynamic() method to make calls dynamic !33 methodNotFound { receiver, name, argumentList, argTypes, call -‐> return makeDynamic(call, int_TYPE) } ! unresolvedProperty { pexp -‐> def lhs = getType(pexp.objectExpression) if (lhs==classNodeFor(MetaClass)) { makeDynamic(pexp, CLOSURE_TYPE) } else { makeDynamic(pexp, int_TYPE) } } ! unresolvedAttribute { pexp -‐> makeDynamic(pexp, int_TYPE) } ! unresolvedVariable { var -‐> makeDynamic(var, int_TYPE) } -
@glaforge — Greach 2014 Type checking extensions — dynamic calls
• New makeDynamic() method to make calls dynamic !33 methodNotFound { receiver, name, argumentList, argTypes, call -‐> return makeDynamic(call, int_TYPE) } ! unresolvedProperty { pexp -‐> def lhs = getType(pexp.objectExpression) if (lhs==classNodeFor(MetaClass)) { makeDynamic(pexp, CLOSURE_TYPE) } else { makeDynamic(pexp, int_TYPE) } } ! unresolvedAttribute { pexp -‐> makeDynamic(pexp, int_TYPE) } ! unresolvedVariable { var -‐> makeDynamic(var, int_TYPE) } Unresolved methods, properties, attributes, or variables can be made dynamic -
@glaforge — Greach 2014 Type checking extensions — dynamic calls
• New makeDynamic() method to make calls dynamic !33 methodNotFound { receiver, name, argumentList, argTypes, call -‐> return makeDynamic(call, int_TYPE) } ! unresolvedProperty { pexp -‐> def lhs = getType(pexp.objectExpression) if (lhs==classNodeFor(MetaClass)) { makeDynamic(pexp, CLOSURE_TYPE) } else { makeDynamic(pexp, int_TYPE) } } ! unresolvedAttribute { pexp -‐> makeDynamic(pexp, int_TYPE) } ! unresolvedVariable { var -‐> makeDynamic(var, int_TYPE) } Unresolved methods, properties, attributes, or variables can be made dynamic Use with care! -
@glaforge — Greach 2014 Remove part of strings with a
regex !35 assert ('Hello Groovy world!' -‐ ~/\s+Gr\w+/) == 'Hello world!' -
@glaforge — Greach 2014 Remove part of strings with a
regex !35 assert ('Hello Groovy world!' -‐ ~/\s+Gr\w+/) == 'Hello world!' A String -
@glaforge — Greach 2014 Remove part of strings with a
regex !35 assert ('Hello Groovy world!' -‐ ~/\s+Gr\w+/) == 'Hello world!' A String A Pattern -
@glaforge — Greach 2014 Remove part of strings with a
regex !35 assert ('Hello Groovy world!' -‐ ~/\s+Gr\w+/) == 'Hello world!' A String A Pattern Matched string fragment removed! -
@glaforge — Greach 2014 Lots of new methods for Iterables
!36 class Counter implements Iterable { private Integer counter = 0 Iterator iterator() { [hasNext: { counter <= 10 }, next: { counter++ }] as Iterator } } -
@glaforge — Greach 2014 Lots of new methods for Iterables
!36 class Counter implements Iterable { private Integer counter = 0 Iterator iterator() { [hasNext: { counter <= 10 }, next: { counter++ }] as Iterator } } assert new Counter().sum() == 55 assert new Counter().min() == 0 assert new Counter().max() == 10 assert new Counter().count(2) == 1 assert new Counter().count { it % 2 == 0 } == 6 assert new Counter().collectMany { [it * 2] } == [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20] assert new Counter().groupBy { it % 2 == 0 ? 'even' : 'odd' } == [even:[0, 2, 4, 6, 8, 10], odd:[1, 3, 5, 7, 9]] -
@glaforge — Greach 2014 Lots of new methods for Iterables
!36 class Counter implements Iterable { private Integer counter = 0 Iterator iterator() { [hasNext: { counter <= 10 }, next: { counter++ }] as Iterator } } assert new Counter().sum() == 55 assert new Counter().min() == 0 assert new Counter().max() == 10 assert new Counter().count(2) == 1 assert new Counter().count { it % 2 == 0 } == 6 assert new Counter().collectMany { [it * 2] } == [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20] assert new Counter().groupBy { it % 2 == 0 ? 'even' : 'odd' } == [even:[0, 2, 4, 6, 8, 10], odd:[1, 3, 5, 7, 9]] Collections are also Iterable -
@glaforge — Greach 2014 @Immutable with copyWith() method !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]' } -
@glaforge — Greach 2014 @Immutable with copyWith() method !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 -
@glaforge — Greach 2014 @Immutable with copyWith() method !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 -
@glaforge — Greach 2014 set() & copyWith() for Date &
Calendar !38 import static java.util.Calendar.MARCH ! def date = new Date().clearTime() date.set(year: 2014, month: MARCH, date: 28) ! def yearLater = date.copyWith(year: 2015) assert yearLater.format('dd-‐MM-‐yyyy') == '28-‐03-‐2015' -
@glaforge — Greach 2014 set() & copyWith() for Date &
Calendar !38 import static java.util.Calendar.MARCH ! def date = new Date().clearTime() date.set(year: 2014, month: MARCH, date: 28) ! def yearLater = date.copyWith(year: 2015) assert yearLater.format('dd-‐MM-‐yyyy') == '28-‐03-‐2015' Mutate -
@glaforge — Greach 2014 set() & copyWith() for Date &
Calendar !38 import static java.util.Calendar.MARCH ! def date = new Date().clearTime() date.set(year: 2014, month: MARCH, date: 28) ! def yearLater = date.copyWith(year: 2015) assert yearLater.format('dd-‐MM-‐yyyy') == '28-‐03-‐2015' def cal = Calendar.instance cal.set(year: 2014, month: MARCH, date: 26) ! def newCalendar = cal.copyWith(date: 27) assert newCalendar.format('dd-‐MM-‐yyyy') == '27-‐03-‐2014' Mutate -
@glaforge — Greach 2014 set() & copyWith() for Date &
Calendar !38 import static java.util.Calendar.MARCH ! def date = new Date().clearTime() date.set(year: 2014, month: MARCH, date: 28) ! def yearLater = date.copyWith(year: 2015) assert yearLater.format('dd-‐MM-‐yyyy') == '28-‐03-‐2015' def cal = Calendar.instance cal.set(year: 2014, month: MARCH, date: 26) ! def newCalendar = cal.copyWith(date: 27) assert newCalendar.format('dd-‐MM-‐yyyy') == '27-‐03-‐2014' Mutate Clone and mutate -
@glaforge — Greach 2014 Loop between Date & Calendar date
points !39 def today = new Date().clearTime() def nextWeek = today + 7 ! today.upto(nextWeek) { println it.format('dd MMMM') } -
@glaforge — Greach 2014 Loop between Date & Calendar date
points !39 def today = new Date().clearTime() def nextWeek = today + 7 ! today.upto(nextWeek) { println it.format('dd MMMM') } Iterate between dates with upto() and downto() -
@glaforge — Greach 2014 Loop between Date & Calendar date
points !39 def today = new Date().clearTime() def nextWeek = today + 7 ! today.upto(nextWeek) { println it.format('dd MMMM') } def to = Calendar.instance to.set(year: 2014, month: Calendar.MARCH, date: 10) ! def from = Calendar.instance from.set(year: 2014, month: Calendar.MARCH, date: 28) ! from.downto(to) { println it.format('dd MMMM') } Iterate between dates with upto() and downto() -
@glaforge — Greach 2014 Loop between Date & Calendar date
points !39 def today = new Date().clearTime() def nextWeek = today + 7 ! today.upto(nextWeek) { println it.format('dd MMMM') } def to = Calendar.instance to.set(year: 2014, month: Calendar.MARCH, date: 10) ! def from = Calendar.instance from.set(year: 2014, month: Calendar.MARCH, date: 28) ! from.downto(to) { println it.format('dd MMMM') } Iterate between dates with upto() and downto() upto() and downto() work with Calendars too -
@glaforge — Greach 2014 Groovysh enhancements — doc command !41
Launches your browser with the JavaDoc and GDK doc of the class -
@glaforge — Greach 2014 Groovysh enhancements — completion !42 Import
completion Method call completion -
@glaforge — Greach 2014 Bintray JCenter repository • 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 -
@glaforge — Greach 2014 Super fast JSON support! ! •
New parsers and builders have been contributed – by Rick Hightower & Andrey Bloschetsov ! • Making Groovy serialization / deserialization faster than – GSON – Jackson !45 -
@glaforge — Greach 2014 A new template engine, markup based
!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() -
@glaforge — Greach 2014 A new template engine, markup based
!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() Type-checked models, bytecode-compiled templates, includes, custom indentation, (un)escaping, i18n support, and more -
@glaforge — Greach 2014 Traits !47 trait FlyingAbility {
String fly() { "I believe I can fly!" } } -
@glaforge — Greach 2014 Traits !47 trait FlyingAbility {
String fly() { "I believe I can fly!" } } A trait keyword applying the @Trait transformation -
@glaforge — Greach 2014 Traits !47 trait FlyingAbility {
String fly() { "I believe I can fly!" } } A trait keyword applying the @Trait transformation class Car implements FlyingAbility {} A class « implements » the trait def c = new Car() assert c.fly() == "I believe I can fly!" -
@glaforge — Greach 2014 Traits characteristics • Groovy traits are
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 -
@glaforge — Greach 2014 Stateful traits !49 trait Named {
String name } ! class Phone implements Named {} ! def phone = new Phone(name: 'Galaxy S3') assert phone.name == 'Galaxy S3' -
@glaforge — Greach 2014 Stateful traits !49 trait Named {
String name } ! class Phone implements Named {} ! def phone = new Phone(name: 'Galaxy S3') assert phone.name == 'Galaxy S3' A usual Groovy property! -
@glaforge — Greach 2014 Trait inheritance !50 trait Named {
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' -
@glaforge — Greach 2014 Trait inheritance !50 trait Named {
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 -
@glaforge — Greach 2014 Runtime traits !51 trait Flying {
String fly() { "I'm flying!" } } ! class Duck {} def d = new Duck() as Flying ! assert d instanceof Flying assert d.fly() == "I'm flying!" -
@glaforge — Greach 2014 Runtime traits !51 trait Flying {
String fly() { "I'm flying!" } } ! class Duck {} def d = new Duck() as Flying ! assert d instanceof Flying assert d.fly() == "I'm flying!" Implement Flying trait at runtime -
@glaforge — Greach 2014 @TailRecursive transformation !52 @groovy.transform.TailRecursive def
fact(n, accu = 1G) { if (n < 2) accu else fact(n -‐ 1, n * accu) } -
@glaforge — Greach 2014 @TailRecursive transformation !52 @groovy.transform.TailRecursive def
fact(n, accu = 1G) { if (n < 2) accu else fact(n -‐ 1, n * accu) } https://github.com/jlink/tailrec -
@glaforge — Greach 2014 @TailRecursive transformation !52 @groovy.transform.TailRecursive def
fact(n, accu = 1G) { if (n < 2) accu else fact(n -‐ 1, n * accu) } https://github.com/jlink/tailrec Transformation applied to methods, not closures -
@glaforge — Greach 2014 New website and documentation • New
reference documentation and guides, using AsciiDoctor ! • New website with a refreshed skin and the new content !53 -
@glaforge — Greach 2014 New website and documentation • New
reference documentation and guides, using AsciiDoctor ! • New website with a refreshed skin and the new content !53 Looking for contributions! -
@glaforge — Greach 2014 GroovyDoc template overhaul !54 GroovyDoc !=
Sexy Doc Would also be nice for Groovy GDK ocumentation -
@glaforge — Greach 2014 Finer-grained implicit closure coercion !56 interface
Action { void act() } ! void atomic(Action a) { a.act() } -
@glaforge — Greach 2014 Finer-grained implicit closure coercion !56 interface
Action { void act() } ! void atomic(Action a) { a.act() } atomic { println "hi" } -
@glaforge — Greach 2014 Finer-grained implicit closure coercion !56 interface
Action { void act() } ! void atomic(Action a) { a.act() } interface ParameterizedAction { void execute(int i) } ! void atomic(ParameterizedAction pa) { pa.execute(1) } atomic { println "hi" } -
@glaforge — Greach 2014 Finer-grained implicit closure coercion !56 interface
Action { void act() } ! void atomic(Action a) { a.act() } interface ParameterizedAction { void execute(int i) } ! void atomic(ParameterizedAction pa) { pa.execute(1) } atomic { println "hi" } We’d get an ambiguous method exception -
@glaforge — Greach 2014 Finer-grained implicit closure coercion !56 interface
Action { void act() } ! void atomic(Action a) { a.act() } interface ParameterizedAction { void execute(int i) } ! void atomic(ParameterizedAction pa) { pa.execute(1) } atomic { println "hi" } atomic { int i -‐> println "hi $i" } We’d get an ambiguous method exception -
@glaforge — Greach 2014 Finer-grained implicit closure coercion !56 interface
Action { void act() } ! void atomic(Action a) { a.act() } interface ParameterizedAction { void execute(int i) } ! void atomic(ParameterizedAction pa) { pa.execute(1) } atomic { println "hi" } atomic { int i -‐> println "hi $i" } We’d get an ambiguous method exception Finer grained coercion to distinguish correct coercion depending on closure’s arguments -
@glaforge — Greach 2014 Goals for the new 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? !60 -
@glaforge — Greach 2014 Antlr v4 grammar • 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! !62 -
@glaforge — Greach 2014 Antlr v4 grammar • 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! !62 -
@glaforge — Greach 2014 Java 8 support • 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 !64