Slide 1

Slide 1 text

The Apache 
 Groovy Keynote! Guillaume Laforge
 @glaforge 
 PMC Chair of 
 Apache Groovy 
 
 Developer Advocate 
 Google Cloud Platform

Slide 2

Slide 2 text

Jay Zimmerman

Slide 3

Slide 3 text

Ready for some pictures?

Slide 4

Slide 4 text

The Bee Gees of my life!

Slide 5

Slide 5 text

The Big G’s of my life!

Slide 6

Slide 6 text

What is the future of Apache Groovy?

Slide 7

Slide 7 text

The Groovy philosophy

Slide 8

Slide 8 text

Java’s cousin

Slide 9

Slide 9 text

A “niche” language?

Slide 10

Slide 10 text

Should we be 
 more aggressive?

Slide 11

Slide 11 text

Should we claim Java’s throne?

Slide 12

Slide 12 text

My precious power feature…

Slide 13

Slide 13 text

Beware feature

Slide 14

Slide 14 text

Exponential complexity

Slide 15

Slide 15 text

The Groovy bloom

Slide 16

Slide 16 text

2M+ downloads a month

Slide 17

Slide 17 text

In the top 20

Slide 18

Slide 18 text

2000+ followers

Slide 19

Slide 19 text

4700+ members

Slide 20

Slide 20 text

400+ members

Slide 21

Slide 21 text

Groovy’s adoption

Slide 22

Slide 22 text

Grails

Slide 23

Slide 23 text

The Gradle elephant in the room

Slide 24

Slide 24 text

The island…

Slide 25

Slide 25 text

The untold Roadmap

Slide 26

Slide 26 text

A new grammar

Slide 27

Slide 27 text

Be followers?

Slide 28

Slide 28 text

Be different?

Slide 29

Slide 29 text

Copy’n paste compatibility

Slide 30

Slide 30 text

With Java 8, Apache Groovy still makes sense?

Slide 31

Slide 31 text

Dynamicity unmatched

Slide 32

Slide 32 text

Little details matter

Slide 33

Slide 33 text

Moving the needle forward

Slide 34

Slide 34 text

Developer productivity

Slide 35

Slide 35 text

Are we just 
 playing 
 catch 
 up?

Slide 36

Slide 36 text

Who’s catching up?

Slide 37

Slide 37 text

Think about JSON!

Slide 38

Slide 38 text

Migrating to Java idioms?

Slide 39

Slide 39 text

Shed some weight?

Slide 40

Slide 40 text

Breaking changes

Slide 41

Slide 41 text

Backward… compatibility matters

Slide 42

Slide 42 text

MOP

Slide 43

Slide 43 text

Jigsaw

Slide 44

Slide 44 text

The dark days

Slide 45

Slide 45 text

Surviving…

Slide 46

Slide 46 text

A competing landscape

Slide 47

Slide 47 text

The Eclipse

Slide 48

Slide 48 text

Paid committers?

Slide 49

Slide 49 text

Two paces…

Slide 50

Slide 50 text

The old guard

Slide 51

Slide 51 text

James Strachan

Slide 52

Slide 52 text

A new hope!

Slide 53

Slide 53 text

You’re the future of Apache Groovy!

Slide 54

Slide 54 text

Show me the code! Groovy 2.4.x, 2.5 and 3.0

Slide 55

Slide 55 text

55 @Canonical becomes a meta-annotation import groovy.transform.*
 
 @Canonical(includeNames = true)
 class Person {
 String name
 int age
 }
 
 assert new Person('Guillaume', 39).toString() ==
 'Person(name:Guillaume, age:39)'

Slide 56

Slide 56 text

55 @Canonical becomes a meta-annotation import groovy.transform.*
 
 @Canonical(includeNames = true)
 class Person {
 String name
 int age
 }
 
 assert new Person('Guillaume', 39).toString() ==
 'Person(name:Guillaume, age:39)' includeNames from @ToString

Slide 57

Slide 57 text

56 More control on annotation collector DUPLICATE Annotations from the annotation collection will always be inserted. PREFER_COLLECTOR Annotations from the collector will be added and any existing annotations with the same name will be removed. PREFER_EXPLICIT Annotations from the collector will be ignored if any existing annotations with the same name are found. PREFER_EXPLICIT_MERGED Annotations from the collector will be ignored if any existing annotations with the same name are found but any new parameters on the collector annotation will be added to existing annotations. PREFER_COLLECTOR_MERGED Annotations from the collector will be added and any existing annotations with the same name will be removed but any new parameters found within existing annotations will be merged into the added annotation.

Slide 58

Slide 58 text

57 Pre/post events for @TupleConstructor import groovy.transform.TupleConstructor @TupleConstructor(pre = { first = first?.toLowerCase() }) class Person { String first } def p = new Person('Jack') assert p.first == 'jack'

Slide 59

Slide 59 text

57 Pre/post events for @TupleConstructor import groovy.transform.TupleConstructor @TupleConstructor(pre = { first = first?.toLowerCase() }) class Person { String first } def p = new Person('Jack') assert p.first == 'jack' import groovy.transform.TupleConstructor import static groovy.test.GroovyAssert.shouldFail @TupleConstructor(pre = { assert first }) class Person { String first } def p = new Person('Jack') shouldFail { def unknown = new Person() }

Slide 60

Slide 60 text

58 Prevent @TupleConstructor default ctors @TupleConstructor
 class Person {
 String first, last
 int age
 }

Slide 61

Slide 61 text

58 Prevent @TupleConstructor default ctors @TupleConstructor
 class Person {
 String first, last
 int age
 } Generates: Person(String first, String last, int age) { /*...*/ } Person(String first, String last) { this(first, last, 0) } Person(String first) { this(first, null) } Person() { this(null) }

Slide 62

Slide 62 text

59 Prevent @TupleConstructor default ctors @TupleConstructor(defaults = true)
 class Person { String first, last
 int age
 }

Slide 63

Slide 63 text

59 Prevent @TupleConstructor default ctors @TupleConstructor(defaults = true)
 class Person { String first, last
 int age
 } Generates only: Person(String first, String last, int age) { /*...*/ }

Slide 64

Slide 64 text

60 New @MapConstructor transformation import groovy.transform.*
 
 @TupleConstructor
 class Person {
 String first, last
 }
 
 @CompileStatic // optional
 @ToString(includeSuperProperties = true)
 @MapConstructor(pre = { super(args?.first, args?.last);
 args = args ?: [:] },
 post = { first = first?.toUpperCase() })
 class Author extends Person {
 String bookName
 } assert new Author(first: 'Dierk', last: 'Koenig', bookName: 'ReGinA').toString() == 'Author(ReGinA, DIERK, Koenig)'
 
 assert new Author().toString() == 'Author(null, null, null)'

Slide 65

Slide 65 text

60 New @MapConstructor transformation import groovy.transform.*
 
 @TupleConstructor
 class Person {
 String first, last
 }
 
 @CompileStatic // optional
 @ToString(includeSuperProperties = true)
 @MapConstructor(pre = { super(args?.first, args?.last);
 args = args ?: [:] },
 post = { first = first?.toUpperCase() })
 class Author extends Person {
 String bookName
 } assert new Author(first: 'Dierk', last: 'Koenig', bookName: 'ReGinA').toString() == 'Author(ReGinA, DIERK, Koenig)'
 
 assert new Author().toString() == 'Author(null, null, null)' Can decorate map ctor with pre / post- instructions

Slide 66

Slide 66 text

61 Properties validated in AST xforms import groovy.transform.AutoClone
 
 @AutoClone(excludes = 'sirName')
 class Person {
 String firstName
 String surName
 }
 
 new Person(firstName: "John", surName: "Doe").clone()

Slide 67

Slide 67 text

61 Properties validated in AST xforms import groovy.transform.AutoClone
 
 @AutoClone(excludes = 'sirName')
 class Person {
 String firstName
 String surName
 }
 
 new Person(firstName: "John", surName: "Doe").clone() Error during @AutoClone processing: 'excludes' property 'sirName' does not exist.

Slide 68

Slide 68 text

62 @Immutable support in class hierarchy import groovy.transform.*
 
 @EqualsAndHashCode
 class Person {
 String name
 }

Slide 69

Slide 69 text

62 @Immutable support in class hierarchy import groovy.transform.*
 
 @EqualsAndHashCode
 class Person {
 String name
 } @Immutable
 @TupleConstructor(includeSuperProperties = true)
 @EqualsAndHashCode(callSuper = true)
 @ToString(includeNames = true, includeSuperProperties = true)
 class Athlete extends Person {
 String sport
 }

Slide 70

Slide 70 text

62 @Immutable support in class hierarchy import groovy.transform.*
 
 @EqualsAndHashCode
 class Person {
 String name
 } @Immutable
 @TupleConstructor(includeSuperProperties = true)
 @EqualsAndHashCode(callSuper = true)
 @ToString(includeNames = true, includeSuperProperties = true)
 class Athlete extends Person {
 String sport
 } def d1 = new Athlete('Michael Jordan', 'BasketBall')
 def d2 = new Athlete(name: 'Roger Federer', sport: 'Tennis') assert d1 != d2
 assert d1.toString() == 
 'Athlete(sport:BasketBall, name:Michael Jordan)'
 assert d2.toString() == 
 'Athlete(sport:Tennis, name:Roger Federer)'

Slide 71

Slide 71 text

63 @Immutable supports Optional import groovy.transform.Immutable
 
 @Immutable
 class Person {
 String name
 Optional address
 }
 
 def p = new Person('Joe', Optional.of('Home'))
 
 assert p.toString() == 'Person(Joe, Optional[Home])'
 assert p.address.get() == 'Home'

Slide 72

Slide 72 text

63 @Immutable supports Optional import groovy.transform.Immutable
 
 @Immutable
 class Person {
 String name
 Optional address
 }
 
 def p = new Person('Joe', Optional.of('Home'))
 
 assert p.toString() == 'Person(Joe, Optional[Home])'
 assert p.address.get() == 'Home' @Immutable
 class Person {
 String name
 Optional birth
 } Fails compilation, as Date is mutable

Slide 73

Slide 73 text

64 New @AutoImplement transformation @AutoImplement
 class MyNames extends AbstractList implements Closeable {}

Slide 74

Slide 74 text

64 New @AutoImplement transformation @AutoImplement
 class MyNames extends AbstractList implements Closeable {} class MyNames extends AbstractList implements Closeable {
 String get(int param0) {
 return null
 }
 boolean addAll(Collection extends String> param0) {
 return false
 }
 void close() throws Exception {
 }
 int size() {
 return 0
 }
 }

Slide 75

Slide 75 text

65 New @AutoImplement transformation @AutoImplement(exception = IOException)
 class MyWriter extends Writer { }

Slide 76

Slide 76 text

65 New @AutoImplement transformation @AutoImplement(exception = IOException)
 class MyWriter extends Writer { } @AutoImplement(exception = UnsupportedOperationException,
 message = 'Not supported by MyIterator')
 class MyIterator implements Iterator { }

Slide 77

Slide 77 text

65 New @AutoImplement transformation @AutoImplement(exception = IOException)
 class MyWriter extends Writer { } @AutoImplement(exception = UnsupportedOperationException,
 message = 'Not supported by MyIterator')
 class MyIterator implements Iterator { } @AutoImplement(code = { throw new UnsupportedOperationException(
 'Should never be called but was called on ' + new Date()) })
 class EmptyIterator implements Iterator {
 boolean hasNext() { false }
 }

Slide 78

Slide 78 text

66 :grab command in groovysh groovy:000> :grab 'com.google.guava:guava:19.0' groovy:000> import com.google.common.collect.BiMap ===> com.google.common.collect.BiMap

Slide 79

Slide 79 text

67 @Delegate on getters too class Person {
 String name @Delegate
 String getName() { name.reverse() } } def p = new Person(name: 'Erine') assert p.toUpperCase() == 'ENIRE'

Slide 80

Slide 80 text

68 JAXB marshalling shortcuts import groovy.transform.EqualsAndHashCode import javax.xml.bind.JAXBContext import javax.xml.bind.annotation.* @EqualsAndHashCode @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement class Person { String name int age }

Slide 81

Slide 81 text

68 JAXB marshalling shortcuts import groovy.transform.EqualsAndHashCode import javax.xml.bind.JAXBContext import javax.xml.bind.annotation.* @EqualsAndHashCode @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement class Person { String name int age } def jaxbContext = JAXBContext.newInstance(Person) def p = new Person(name: 'Marion', age: 8) def xml = jaxbContext.marshal(p) assert jaxbContext.unmarshal(xml, Person) == p

Slide 82

Slide 82 text

69 CliBuilder supports annotations // specify parameters
 def cli = new CliBuilder(usage: 'groovy Greeter [option]')
 cli.a(longOpt: 'audience', args: 1, 'greeting audience')
 cli.h(longOpt: 'help', 'display usage')
 
 // parse and process parameters
 def options = cli.parse(args)
 if (options.h) cli.usage()
 else println "Hello ${options.a ? options.a : 'World'}"

Slide 83

Slide 83 text

70 CliBuilder supports annotations import groovy.cli.*
 
 interface GreeterI {
 @Option(shortName='h', description='display usage') Boolean help()
 @Option(shortName='a', description='greeting audience') String audience()
 @Unparsed List remaining()
 }

Slide 84

Slide 84 text

70 CliBuilder supports annotations import groovy.cli.*
 
 interface GreeterI {
 @Option(shortName='h', description='display usage') Boolean help()
 @Option(shortName='a', description='greeting audience') String audience()
 @Unparsed List remaining()
 } def cli = new CliBuilder(usage: 'groovy Greeter [option]')
 def argz = '--audience Groovologist'.split()
 def options = cli.parseFromSpec(GreeterI, argz)
 assert options.audience() == 'Groovologist'

Slide 85

Slide 85 text

71 With… tap… @Canonical
 class Person {
 String name
 int age
 }

Slide 86

Slide 86 text

71 With… tap… assert new Person().with { name = 'Guillaume' age = 39 return it }.toString() == 'Person(Guillaume, 39)' @Canonical
 class Person {
 String name
 int age
 }

Slide 87

Slide 87 text

71 With… tap… assert new Person().with { name = 'Guillaume' age = 39 return it }.toString() == 'Person(Guillaume, 39)' assert new Person().tap { name = 'Guillaume' age = 39 }.toString() == 'Person(Guillaume, 39)' @Canonical
 class Person {
 String name
 int age
 }

Slide 88

Slide 88 text

No content

Slide 89

Slide 89 text

JSON generator

Slide 90

Slide 90 text

73 A customizable JSON serializer class Person {
 String name
 String title
 int age
 String password
 Date dob
 URL favoriteUrl
 } Person person = new Person(name: 'John', title: null, 
 age: 21, password: 'secret',
 dob: Date.parse('yyyy-MM-dd', '1984-12-15'),
 favoriteUrl: new URL('http://groovy-lang.org/'))
 
 def generator = new JsonGenerator.Options()
 .excludeNulls()
 .dateFormat('yyyy@MM')
 .excludeFieldsByName('age', 'password')
 .excludeFieldsByType(URL)
 .build()
 
 assert generator.toJson(person) == 
 '{"dob":"1984@12","name":"John"}'

Slide 91

Slide 91 text

74 A customizable JSON serializer def generator = new JsonGenerator.Options()
 .addConverter(URL) { URL u, String key ->
 if (key == 'favoriteUrl') {
 u.getHost()
 } else {
 u
 }
 }
 .build()

Slide 92

Slide 92 text

74 A customizable JSON serializer def generator = new JsonGenerator.Options()
 .addConverter(URL) { URL u, String key ->
 if (key == 'favoriteUrl') {
 u.getHost()
 } else {
 u
 }
 }
 .build() Data type to customize

Slide 93

Slide 93 text

74 A customizable JSON serializer def generator = new JsonGenerator.Options()
 .addConverter(URL) { URL u, String key ->
 if (key == 'favoriteUrl') {
 u.getHost()
 } else {
 u
 }
 }
 .build() Data type to customize Optional key

Slide 94

Slide 94 text

No content

Slide 95

Slide 95 text

AST macros

Slide 96

Slide 96 text

76 AST macros: the old way @Retention(RetentionPolicy.SOURCE)
 @Target([ElementType.TYPE])
 @GroovyASTTransformationClass([ "metaprogramming.AddMethodASTTransformation"]) @interface AddMethod { }

Slide 97

Slide 97 text

76 AST macros: the old way @Retention(RetentionPolicy.SOURCE)
 @Target([ElementType.TYPE])
 @GroovyASTTransformationClass([ "metaprogramming.AddMethodASTTransformation"]) @interface AddMethod { } Let’s decorate a class 
 with an additional method

Slide 98

Slide 98 text

77 AST macros: the old way @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)
 class AddMethodASTTransformation extends AbstractASTTransformation {
 @Override
 void visit(ASTNode[] nodes, SourceUnit source) {
 ClassNode classNode = (ClassNode) nodes[1]
 
 ReturnStatement code =
 new ReturnStatement(
 new ConstantExpression("42"))
 
 MethodNode methodNode =
 new MethodNode("getMessage",
 ACC_PUBLIC, ClassHelper.make(String),
 [] as Parameter[], [] as ClassNode[],
 code)
 
 classNode.addMethod(methodNode)
 }
 }

Slide 99

Slide 99 text

77 AST macros: the old way @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)
 class AddMethodASTTransformation extends AbstractASTTransformation {
 @Override
 void visit(ASTNode[] nodes, SourceUnit source) {
 ClassNode classNode = (ClassNode) nodes[1]
 
 ReturnStatement code =
 new ReturnStatement(
 new ConstantExpression("42"))
 
 MethodNode methodNode =
 new MethodNode("getMessage",
 ACC_PUBLIC, ClassHelper.make(String),
 [] as Parameter[], [] as ClassNode[],
 code)
 
 classNode.addMethod(methodNode)
 }
 } return 42

Slide 100

Slide 100 text

78 AST macros: the new way @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)
 class AddMethodASTTransformation extends AbstractASTTransformation {
 @Override
 void visit(ASTNode[] nodes, SourceUnit source) {
 ClassNode classNode = (ClassNode) nodes[1]
 
 ReturnStatement simplestCode = macro { return "42" } 
 
 
 MethodNode methodNode =
 new MethodNode("getMessage",
 ACC_PUBLIC, ClassHelper.make(String),
 [] as Parameter[], [] as ClassNode[],
 code)
 
 classNode.addMethod(methodNode)
 }
 }

Slide 101

Slide 101 text

78 AST macros: the new way @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)
 class AddMethodASTTransformation extends AbstractASTTransformation {
 @Override
 void visit(ASTNode[] nodes, SourceUnit source) {
 ClassNode classNode = (ClassNode) nodes[1]
 
 ReturnStatement simplestCode = macro { return "42" } 
 
 
 MethodNode methodNode =
 new MethodNode("getMessage",
 ACC_PUBLIC, ClassHelper.make(String),
 [] as Parameter[], [] as ClassNode[],
 code)
 
 classNode.addMethod(methodNode)
 }
 } macro method

Slide 102

Slide 102 text

78 AST macros: the new way @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)
 class AddMethodASTTransformation extends AbstractASTTransformation {
 @Override
 void visit(ASTNode[] nodes, SourceUnit source) {
 ClassNode classNode = (ClassNode) nodes[1]
 
 ReturnStatement simplestCode = macro { return "42" } 
 
 
 MethodNode methodNode =
 new MethodNode("getMessage",
 ACC_PUBLIC, ClassHelper.make(String),
 [] as Parameter[], [] as ClassNode[],
 code)
 
 classNode.addMethod(methodNode)
 }
 } macro method Different variants: macro(Closure), macro(boolean, Closure) macro(CompilePhase, Closure)…

Slide 103

Slide 103 text

79 AST macros: variable substitution @Retention(RetentionPolicy.SOURCE)
 @Target([ElementType.FIELD])
 @GroovyASTTransformationClass([ "metaprogramming.MD5ASTTransformation"])
 @interface MD5 { }

Slide 104

Slide 104 text

79 AST macros: variable substitution @Retention(RetentionPolicy.SOURCE)
 @Target([ElementType.FIELD])
 @GroovyASTTransformationClass([ "metaprogramming.MD5ASTTransformation"])
 @interface MD5 { } Adds a get${stringGield}MD5() method for each @MD5 String field

Slide 105

Slide 105 text

80 AST macros: variable substitution BlockStatement buildMD5MethodCode(FieldNode fieldNode) {
 VariableExpression fieldVar = GeneralUtils.varX(fieldNode.name)
 
 return macro(CompilePhase.SEMANTIC_ANALYSIS, true) {
 return java.security.MessageDigest
 .getInstance('MD5')
 .digest($v { fieldVar }.getBytes())
 .encodeHex()
 .toString()
 }
 }

Slide 106

Slide 106 text

80 AST macros: variable substitution BlockStatement buildMD5MethodCode(FieldNode fieldNode) {
 VariableExpression fieldVar = GeneralUtils.varX(fieldNode.name)
 
 return macro(CompilePhase.SEMANTIC_ANALYSIS, true) {
 return java.security.MessageDigest
 .getInstance('MD5')
 .digest($v { fieldVar }.getBytes())
 .encodeHex()
 .toString()
 }
 } Substitute embedded $v value with 
 a variable from the context

Slide 107

Slide 107 text

81 AST macros: macro class @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)
 class StatisticsASTTransformation extends AbstractASTTransformation {
 void visit(ASTNode[] nodes, SourceUnit source) {
 ClassNode classNode = (ClassNode) nodes[1]
 ClassNode templateClass = buildTemplateClass(classNode)
 
 templateClass.methods.each { MethodNode node ->
 classNode.addMethod(node)
 }
 }
 
 ClassNode buildTemplateClass(ClassNode reference) {
 def methodCount = constX(reference.methods.size())
 def fieldCount = constX(reference.fields.size())
 
 return new MacroClass() {
 class Statistics {
 java.lang.Integer getMethodCount() {
 return $v { methodCount }
 }
 
 java.lang.Integer getFieldCount() {
 return $v { fieldCount }
 }


Slide 108

Slide 108 text

81 AST macros: macro class @GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)
 class StatisticsASTTransformation extends AbstractASTTransformation {
 void visit(ASTNode[] nodes, SourceUnit source) {
 ClassNode classNode = (ClassNode) nodes[1]
 ClassNode templateClass = buildTemplateClass(classNode)
 
 templateClass.methods.each { MethodNode node ->
 classNode.addMethod(node)
 }
 }
 
 ClassNode buildTemplateClass(ClassNode reference) {
 def methodCount = constX(reference.methods.size())
 def fieldCount = constX(reference.fields.size())
 
 return new MacroClass() {
 class Statistics {
 java.lang.Integer getMethodCount() {
 return $v { methodCount }
 }
 
 java.lang.Integer getFieldCount() {
 return $v { fieldCount }
 }
 MacroClass not restricted to expressions & statements: you can generate methods & classes

Slide 109

Slide 109 text

82 AST matchers @Retention(RetentionPolicy.SOURCE)
 @Target([ElementType.METHOD])
 @GroovyASTTransformationClass(["JokingASTTransformation"])
 @interface Joking { }

Slide 110

Slide 110 text

82 AST matchers @Retention(RetentionPolicy.SOURCE)
 @Target([ElementType.METHOD])
 @GroovyASTTransformationClass(["JokingASTTransformation"])
 @interface Joking { } @GroovyASTTransformation( phase = CompilePhase.INSTRUCTION_SELECTION)
 class JokingASTTransformation extends AbstractASTTransformation {
 void visit(ASTNode[] nodes, SourceUnit source) {
 MethodNode methodNode = nodes[1]
 
 methodNode
 .getCode()
 .visit(
 new ConvertOnePlusOneToThree(source))
 }
 }

Slide 111

Slide 111 text

82 AST matchers @Retention(RetentionPolicy.SOURCE)
 @Target([ElementType.METHOD])
 @GroovyASTTransformationClass(["JokingASTTransformation"])
 @interface Joking { } @GroovyASTTransformation( phase = CompilePhase.INSTRUCTION_SELECTION)
 class JokingASTTransformation extends AbstractASTTransformation {
 void visit(ASTNode[] nodes, SourceUnit source) {
 MethodNode methodNode = nodes[1]
 
 methodNode
 .getCode()
 .visit(
 new ConvertOnePlusOneToThree(source))
 }
 } class ConvertOnePlusOneToThree extends ClassCodeExpressionTransformer {
 SourceUnit sourceUnit
 
 ConvertOnePlusOneToThree(SourceUnit sourceUnit) {
 this.sourceUnit = sourceUnit
 }
 
 Expression transform(Expression exp) {
 Expression ref = macro { 1 + 1 }
 
 if (ASTMatcher.matches(ref, exp)) {
 return macro { 3 }
 }
 
 return super.transform(exp)
 }
 }

Slide 112

Slide 112 text

83 AST matchers void testTestingSumExpression() {
 use(ASTMatcher) {
 TwiceASTTransformation sample = new TwiceASTTransformation() 
 Expression referenceNode = macro {
 a + a
 }.withConstraints {
 placeholder 'a'
 }
 
 assert sample
 .sumExpression
 .matches(referenceNode)
 }
 }

Slide 113

Slide 113 text

83 AST matchers void testTestingSumExpression() {
 use(ASTMatcher) {
 TwiceASTTransformation sample = new TwiceASTTransformation() 
 Expression referenceNode = macro {
 a + a
 }.withConstraints {
 placeholder 'a'
 }
 
 assert sample
 .sumExpression
 .matches(referenceNode)
 }
 } Placeholders to match anything

Slide 114

Slide 114 text

No content

Slide 115

Slide 115 text

Groovy “Parrot”!

Slide 116

Slide 116 text

85 Good ol’ do / while loops int i = 0
 
 do {
 println i++
 } while (i < 10)
 
 assert i == 10

Slide 117

Slide 117 text

86 Identity comparison is back! class Person { String name }
 
 def p1 = new Person(name: 'Marion')
 def p2 = new Person(name: 'Érine')
 def p2copy = p2 
 
 assert p2 === p2copy
 assert p1 !== p2

Slide 118

Slide 118 text

87 !in and !instanceof if (!(1324 instanceof String)) {}
 if (1234 !instanceof String) {}
 
 assert !(3 in [])
 assert 3 !in []

Slide 119

Slide 119 text

88 Safe indexing ?[ ] List list = null
 assert list?[2] == null Map map = null
 assert map?['abc'] == null

Slide 120

Slide 120 text

89 Try with resources import java.util.zip.GZIPOutputStream
 
 def input = new File('./NOTICE')
 def output = new File('/tmp/zipped.zip')
 
 // Java-style
 try (
 FileInputStream fin = new FileInputStream(input);
 GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(output))
 ) {
 byte[] buffer = new byte[4096]
 int nread = 0
 while ((nread = fin.read(buffer)) != -1) {
 out.write(buffer, 0, nread)
 }
 }

Slide 121

Slide 121 text

90 Try with resources import java.util.zip.GZIPOutputStream
 
 def input = new File('./NOTICE')
 def output = new File('/tmp/zipped.zip')
 
 // Groovy-style :-)
 try (
 fin = new FileInputStream(input)
 out = new GZIPOutputStream(new FileOutputStream(output))
 ) { out << fin }

Slide 122

Slide 122 text

91 Try with resources import java.util.zip.GZIPOutputStream
 
 def input = new File('./NOTICE')
 def output = new File('/tmp/zipped.zip')
 
 // Groovy-style :-)
 
 
 
 out << fin

Slide 123

Slide 123 text

92 Lambdas! import static java.util.stream.Collectors.toList
 
 (1..10).forEach((it) -> { println it })
 
 assert (1..10).stream()
 .filter((it) -> it % 2 == 0)
 .map((it) -> it * 2)
 .collect(toList()) == [4, 8, 12, 16, 20]

Slide 124

Slide 124 text

93 Lambdas! // all the shapes (x, y) -> x + y
 (x, y) -> { x + y }
 (int x, int y) -> x + y

Slide 125

Slide 125 text

93 Lambdas! // all the shapes (x, y) -> x + y
 (x, y) -> { x + y }
 (int x, int y) -> x + y implicit return

Slide 126

Slide 126 text

93 Lambdas! // all the shapes (x, y) -> x + y
 (x, y) -> { x + y }
 (int x, int y) -> x + y def c = (int x, int y = 0) -> x + y
 assert c(1) == 1 implicit return

Slide 127

Slide 127 text

93 Lambdas! // all the shapes (x, y) -> x + y
 (x, y) -> { x + y }
 (int x, int y) -> x + y def c = (int x, int y = 0) -> x + y
 assert c(1) == 1 implicit return default argument

Slide 128

Slide 128 text

94 Method references — class refs import static java.util.stream.Collectors.toList
 
 // class::staticMethod
 assert ['1', '2', '3'] ==
 [1, 2, 3].stream()
 .map(Integer::toString)
 .collect(toList())
 
 // class::instanceMethod
 assert ['A', 'B', 'C'] ==
 ['a', 'b', 'c'].stream()
 .map(String::toUpperCase)
 .collect(toList())

Slide 129

Slide 129 text

95 Method references — instance refs class Name {
 String name
 char firstLetter() { name[0] }
 static cloneOf(Name n) { new Name(name: n.name + '2') }
 }

Slide 130

Slide 130 text

95 Method references — instance refs class Name {
 String name
 char firstLetter() { name[0] }
 static cloneOf(Name n) { new Name(name: n.name + '2') }
 } // instance::instanceMethod
 def fl = new Name(name: 'Guillaume')::firstLetter
 assert fl() == 'G'
 
 // instance::staticMethod
 def n = new Name(name: 'Marion')
 def clone = n::cloneOf
 assert clone(n).name == 'Marion2'

Slide 131

Slide 131 text

96 Method references — constructor refs def r = Random::new
 println r().nextInt(10)

Slide 132

Slide 132 text

96 Method references — constructor refs def r = Random::new
 println r().nextInt(10) def arr2d = String[][]::new
 arr2d(2, 2) == [[null, null], [null, null]]

Slide 133

Slide 133 text

97 Method references — constructor refs import groovy.transform.Canonical
 
 @Canonical
 class Animal {
 String kind
 }

Slide 134

Slide 134 text

97 Method references — constructor refs import groovy.transform.Canonical
 
 @Canonical
 class Animal {
 String kind
 } def a = Animal::new
 assert a('lion').kind == 'lion'
 
 def c = Animal
 assert c::new('cat').kind == 'cat'

Slide 135

Slide 135 text

98 Elvis assignment — Not yet there! import groovy.transform.ToString
 
 @ToString
 class Person {
 String name
 int age
 } def p = new Person(name: 'Érine')
 
 p.with {
 name = name ?: 'unknown'
 age = age ?: 3
 }
 
 assert p.toString() == 'Person(Érine, 3)'

Slide 136

Slide 136 text

98 Elvis assignment — Not yet there! import groovy.transform.ToString
 
 @ToString
 class Person {
 String name
 int age
 } def p = new Person(name: 'Érine')
 
 p.with {
 name = name ?: 'unknown'
 age = age ?: 3
 }
 
 assert p.toString() == 'Person(Érine, 3)' p.with {
 name ?= 'unknown'
 age ?= 3
 }

Slide 137

Slide 137 text

99 JavaDoc comments saved import org.codehaus.groovy.control.*
 import static org.apache.groovy.parser.antlr4.AstBuilder.DOC_COMMENT
 
 def code = '''
 /**
 * class Foo
 * @author glaforge
 */
 class Foo {
 /** a method */
 void m(int i) {}
 }
 '''

Slide 138

Slide 138 text

99 JavaDoc comments saved import org.codehaus.groovy.control.*
 import static org.apache.groovy.parser.antlr4.AstBuilder.DOC_COMMENT
 
 def code = '''
 /**
 * class Foo
 * @author glaforge
 */
 class Foo {
 /** a method */
 void m(int i) {}
 }
 ''' def ast = new CompilationUnit().tap {
 addSource 'methodM.groovy', code
 compile Phases.SEMANTIC_ANALYSIS
 }.ast
 
 def cls = ast.classes[0] 
 assert cls.nodeMetaData[DOC_COMMENT] .contains('class Foo')
 assert cls.methods[0].nodeMetaData[DOC_COMMENT] .contains('a method')

Slide 139

Slide 139 text

Thanks for your attention! Enjoy G3 Summit!

Slide 140

Slide 140 text

101 Picture credits The elephant in the room http://www.elephantsinthelivingroom.org/backgrounds/elephant-in-room.jpg Road https://pixabay.com/fr/route-marquage-de-route-rue-miles-166543/ Prague https://pixabay.com/fr/prague-r%C3%A9publique-tch%C3%A8que-ville-1168302/ Wise men http://img03.deviantart.net/4431/i/2012/044/f/6/three_wise_men_color_by_csoro-d4pmlv2.jpg Rocket lift-off https://pixabay.com/fr/fus%C3%A9e-ses-9-lancement-cap-canaveral-1245696/ Java beans https://pixabay.com/fr/grains-de-caf%C3%A9-caf%C3%A9-caf%C3%A9ine-boire-167001/ Ring http://www.zastavki.com/pictures/originals/2013/Movies__037523_.jpg Letter G https://pixabay.com/fr/typographie-geschtaltung-fontes-1069409/ Grammar https://pixabay.com/fr/grammaire-abc-dictionnaire-mots-390029/

Slide 141

Slide 141 text

102 Picture credits Copy and paste https://d262ilb51hltx0.cloudfront.net/max/2000/1*sglxoCq-p9gbefsUfGaNlg.jpeg A new hope http://static.dolimg.com/lucas/movies/starwars/starwars_epi4_01-4f17cf46f1dd.jpg Snails https://pixabay.com/fr/escargots-course-gast%C3%A9ropodes-1753611/ The revenant https://static.independent.co.uk/s3fs-public/thumbnails/image/2016/02/18/16/The-Revenant-Leo.jpg Broken window https://pixabay.com/fr/endroits-perdus-rompu-fen%C3%AAtre-verre-1798639/ Ducklings https://pixabay.com/fr/canard-canetons-les-b%C3%A9b%C3%A9s-caneton-207344/ Jigsaw https://pixabay.com/fr/pi%C3%A8ces-du-puzzle-mix-les-mains-592798/ Bee Gees http://cdn-mag.itcher.com/mag/wp-content/uploads/2015/06/BeeGees.jpg Buddha https://pixabay.com/fr/bouddha-le-bouddhisme-statue-525883/

Slide 142

Slide 142 text

103 Picture credits Cousin https://pixabay.com/fr/cousins-jouer-l-ext%C3%A9rieur-jouets-1587250/ Dollar https://pixabay.com/fr/dollar-billet-de-banque-%C3%A9tats-unis-1161782/ Flower bloom https://pixabay.com/fr/fleur-de-printemps-arbre-nature-289844/ Baby shoes https://pixabay.com/fr/chaussures-grossesse-enfant-505471/ Grails https://isimbolinellacomunicazione.files.wordpress.com/2013/02/calice_du_sacre_tau.jpg Eclipse https://pixabay.com/fr/%C3%A9clipses-solaire-soleil-434338/ Baseball catch up https://pixabay.com/fr/baseball-lecteur-jeu-jouer-1504968/ Dogs https://pixabay.com/fr/chiens-matraques-jouer-morsure-708354/ Niche / kennel https://pixabay.com/fr/chien-niche-cage-peluche-1199846/

Slide 143

Slide 143 text

104 Picture credits Fist punch https://pixabay.com/fr/poing-coup-puissance-wrestling-1561157/ Throne http://static.srcdn.com/wp-content/uploads/GoT-Players-Iron-Throne-Feature.jpg Migrating birds https://pixabay.com/fr/troupeau-flocage-oies-migrer-vol-378676/ Trash https://pixabay.com/fr/poubelle-garbage-seau-vert-1111448/ MOP http://i.huffpost.com/gen/1176457/images/o-CLEAN-MOP-facebook.jpg Backward snow gliding https://pixabay.com/fr/enfant-fille-bob-rouler-sur-1214231/ Rabbit & tortoise https://media.senscritique.com/media/000008120110/1200/Le_Lievre_et_la_Tortue.jpg Car lights https://pixabay.com/fr/autoroute-photo-de-nuit-feux-nuit-393492/ Agility http://wallpoper.com/images/00/15/50/09/lifestyle-female-dynamic-movement-of-women_00155009.jpg

Slide 144

Slide 144 text

105 Picture credits Watch complication https://gildedholdings.files.wordpress.com/2014/10/lange-grand-complication-uhrwerk-movement-1-neu.jpg Speed meter https://pixabay.com/fr/odom%C3%A8tre-1810107/ Swiss army knife https://pixabay.com/fr/arm%C3%A9e-de-terre-lame-compact-coup%C3%A9-15563/ Punk https://pixabay.com/fr/jeune-jeune-fille-jeunesse-punk-175504/ Jason http://static.comicvine.com/uploads/original/10/103184/3150852-2994250-0447705879-jason.jpg Creep http://orig11.deviantart.net/f87a/f/2012/092/1/5/creep_by_holavengoaflotar-d4urypg.jpg Jammed wires https://www.exchangewire.com/wp-content/uploads/2016/09/7670055210_ceb0c9ef9a_b.jpg