Slide 1

Slide 1 text

objectcomputing.com © 2020, Object Computing, Inc. (OCI). All rights reserved. No part of these notes may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior, written permission of Object Computing, Inc. (OCI) A Groovy Day of Training Dr Paul King OCI Groovy Lead V.P. and PMC Chair Apache Groovy @paulk_asert © 2020 Object Computing, Inc. (OCI). All rights reserved. objectcomputing.com

Slide 2

Slide 2 text

Dr Paul King OCI Groovy Lead V.P. and PMC Chair Apache Groovy Slides: https://speakerdeck.com/paulk/groovy-day-training Supplementary slides: https://speakerdeck.com/paulk/make-your-testing-groovy Examples repo: https://github.com/paulk-asert/groovy-day-training ReGinA author: https://www.manning.com/books/groovy-in-action-second-edition Twitter: @paulk_asert

Slide 3

Slide 3 text

Friends of Apache Groovy Open Collective

Slide 4

Slide 4 text

History: Baking the Groovy language circa 2003 Ingredients and requirements • A healthy serving of Java inspiration • A dollop or two from Ruby, Smalltalk, Python, JavaScript • Some sprinkles from Haskell, Scala, C# • Pain points: scripting, functions, XML, SQL, dynamic capabilities, missing features Java Groovy

Slide 5

Slide 5 text

History: Why Groovy? • Java perceived as good for core library development • But there were some pain points © ASERT 2006-2020 Java

Slide 6

Slide 6 text

History: Perceived Java pain points © ASERT 2006-2020 Verbose & complex for common design patterns, XML, SQL, scripting, … Limited functional programming Limited concurrency options Evolves too slowly I need feature XXX Limited flexibility Java Missing features

Slide 7

Slide 7 text

History: Options to overcome Java pain points © ASERT 2006-2020 Verbose & complex for common design patterns, XML, SQL, scripting, … Limited functional programming Limited concurrency options Evolves too slowly I need feature XXX Limited flexibility Java Missing features Haskell Shell scripts Apache Commons Erlang XSLT Scala Ruby Python Perl Other library Lobby to fix Java Wait for new versions

Slide 8

Slide 8 text

History: Groovy as an elixir to Java pain points © ASERT 2006-2020 Verbose & complex for common design patterns, XML, SQL, scripting, … Limited functional programming Limited concurrency options Evolves too slowly I need feature XXX Limited flexibility Java Missing features Groovy

Slide 9

Slide 9 text

History: Groovy evolution © ASERT 2006-2020 Verbose & complex for common design patterns, XML, SQL, scripting, … Limited functional programming Limited concurrency options Evolves too slowly I need feature XXX Limited flexibility Java Missing features Groovy TIOBE index October 2020: 11th

Slide 10

Slide 10 text

Groovy by the numbers: Contributors and more

Slide 11

Slide 11 text

Groovy by the numbers: Highlights Initial release – dynamic companion for Java Elvis op, joint compiler, ExpandoMetaClass Multi-assignment, grapes, AST transforms Power asserts, AST browser Command chains, functional enhancements Static nature, modules, indy support Meta-annotations, type checker extensions SAM coercion, DSL & groovysh improvements Closure type inference, traits, JDK8 Android support, many small improvements AST reworking, macros, macro methods Parrot parser (inc lambdas), split packaging

Slide 12

Slide 12 text

Groovy by the numbers: Releases

Slide 13

Slide 13 text

Groovy by the numbers: Downloads Popular & growing 2016: 23M 2017: 50M 2018: 103M 2019: 197M 2020h1: 147M currently: just over 30M+ per month Will be close to ¾ billion downloads by year end

Slide 14

Slide 14 text

Groovy by the numbers: Enhancements ❖ On-going innovation

Slide 15

Slide 15 text

3.0 highlights

Slide 16

Slide 16 text

Groovy 3.0 Themes ❖ Parrot parser ❖ Improved copy/paste with Java ❖ New syntax/operators ❖ JDK8 minimum and better JDK 9/10 JPMS support ❖ Other improvements ❖ GDK additions ❖ Additional documentation options

Slide 17

Slide 17 text

Groovy 3.0 Themes ❖ Parrot parser ❖ Improved copy/paste with Java ❖ New syntax/operators ❖ JDK8 minimum and better JDK 9/10 JPMS support ❖ Other improvements ❖ GDK additions ❖ Additional documentation options

Slide 18

Slide 18 text

Groovy 3.0 Themes ❖ Parrot parser ❖ Improved copy/paste with Java ❖ New syntax/operators ❖ JDK8 minimum and better JDK 9/10 JPMS support ❖ Other improvements ❖ GDK additions ❖ Additional documentation options (comments in AST/embedded)

Slide 19

Slide 19 text

New operators: identity/negated variants def first = [1, 2, 3] def second = [1, 2, 3] def alsoFirst = first assert first.equals(second) assert first == second assert first.is(alsoFirst) assert first === alsoFirst assert first !== second

Slide 20

Slide 20 text

New operators: negated variants for instanceof/in assert !('foo' instanceof Date) // Java/Groovy 1+ assert 'bar' !instanceof Date // Groovy 3 def nums = [1, 2, 3] assert 4 !in nums

Slide 21

Slide 21 text

New operators: Elvis assignment // Java/Groovy 1+ ternary operator def (a, b) = [10, null] a = a != null ? a : 20 b = b != null ? b : 30 assert a == 10 && b == 30

Slide 22

Slide 22 text

// Java/Groovy 1+ ternary operator def (a, b) = [10, null] a = a != null ? a : 20 b = b != null ? b : 30 assert a == 10 && b == 30 New operators: Elvis assignment // Groovy 2 Elvis operator def (c, d) = [100, null] c = c ?: 200 d = d ?: 300 assert c == 100 && d == 300 // Groovy 3 Elvis assignment operator def (e, f) = [1000, null] e ?= 2000 f ?= 3000 assert e == 1000 && f == 3000

Slide 23

Slide 23 text

New operators: Safe navigation (Groovy 1+) def (a, b) = ['foo', null] assert a?.size() == 3 assert b?.size() == null // but no NPE thrown

Slide 24

Slide 24 text

New operators: Safe indexing def as = ['foo', 'bar'] def bs = null assert as?[0] == 'foo' assert bs?[0] == null def (a, b) = ['foo', null] assert a?.size() == 3 assert b?.size() == null

Slide 25

Slide 25 text

New operators: Safe indexing def as = ['foo', 'bar'] def bs = null assert as?[0] == 'foo' assert bs?[0] == null def (a, b) = ['foo', null] assert a?.size() == 3 assert b?.size() == null String[] as = ['foo', 'bar'] String[] bs = null assert as?[0] == 'foo' assert bs?[0] == null def as = [foo: 10, bar: 20] def bs = null assert as?['foo'] == 10 assert bs?['anything'] == null

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

Method ref examples: class variants with streams import java.util.stream.Stream import static java.util.stream.Collectors.toList // class::staticMethod assert ['1', '2', '3'] == Stream.of(1, 2, 3) .map(String::valueOf) .collect(toList()) // class::instanceMethod assert ['A', 'B', 'C'] == ['a', 'b', 'c'].stream() .map(String::toUpperCase) .collect(toList())

Slide 28

Slide 28 text

More method ref examples: constructors // normal constructor def r = Random::new assert r().nextInt(10) in 0..9 // array constructor is handy when working with various Java libraries, e.g. streams assert [1, 2, 3].stream().toArray().class.name == '[Ljava.lang.Object;' assert [1, 2, 3].stream().toArray(Integer[]::new).class.name == '[Ljava.lang.Integer;' // works with multi-dimensional arrays too def make2d = String[][]::new def tictac = make2d(3, 3) tictac[0] = ['X', 'O', 'X'] tictac[1] = ['X', 'X', 'O'] tictac[2] = ['O', 'X', 'O'] assert tictac*.join().join('\n') == ''' XOX XXO OXO '''.trim()

Slide 29

Slide 29 text

Method closures & method references @groovy.transform.CompileStatic def method() { Function lower = String::toLowerCase println 'lower = ' + lower } // lower = MethodRefG$$Lambda$135/0x0000000801376440@5411dd90 def upper = String.&toUpperCase println 'upper = ' + upper // upper = org.codehaus.groovy.runtime.MethodClosure@7aa9e414 def plus = BigInteger.&add def fivePlus = plus.curry(5G).memoizeAtLeast(10) assert fivePlus(3G) == 8G assert fivePlus << 4G == 9G

Slide 30

Slide 30 text

Parrot: Java-style Lambdas and var reserved type // Closure Groovy 1+ def two = 2 def twice = { x -> x * two } assert 6 == twice.call(3) assert 4 == twice(two) // Lambdas, var for local variables/lambda params (Java 8, 10/11) var three = 3 IntFunction thrice = (final var x) -> x * three assert 6 == thrice.apply(two) assert 9 == thrice(three)

Slide 31

Slide 31 text

Parrot: Closures, streams and method references def two = 2 def twice = { x -> x * two } def nums = [1, 2, 3] def expectedTwice = [2, 4, 6] assert expectedTwice == nums.collect { i -> twice.call(i) } assert expectedTwice == nums.collect { twice(it) } assert expectedTwice == nums.stream() .map { i -> twice(i) } .collect(Collectors.toList()) assert expectedTwice == nums.stream().map { twice(it) }.toList() assert expectedTwice == nums.collect(twice::call) assert expectedTwice == nums.stream().map(twice::call).toList() assert expectedTwice as Integer[] == nums.stream().map(twice::call).toArray(String[]::new) assert expectedTwice as Integer[] == nums.stream().map(twice::call).toArray(Integer)

Slide 32

Slide 32 text

Parrot: Java-style Lambdas and streams var three = 3 IntFunction thrice = (final var x) -> x * three def expectedThrice = [3, 6, 9] assert expectedThrice == nums.collect(thrice::apply) assert expectedThrice == nums.collect(i -> thrice(i)) assert expectedThrice == nums.collect(i -> thrice.apply(i)) assert expectedThrice == nums.collect { i -> thrice.apply(i) } assert expectedThrice == nums.collect { i -> thrice(i) } assert expectedThrice == nums.collect { thrice(it) } assert expectedThrice == nums.stream().map(thrice::apply).toList() assert expectedThrice == nums.stream().map(i -> thrice.apply(i)).toList() assert expectedThrice == nums.stream().map(i -> thrice(i)).toList() assert expectedThrice == nums.stream().map { i -> thrice.apply(i) }.toList() assert expectedThrice == nums.stream().map { i -> thrice(i) }.toList() assert expectedThrice == nums.stream().map { thrice(it) }.toList()

Slide 33

Slide 33 text

Parrot: Better Java syntax support • Try with resources • Enhanced try with resources (JDK9) • Arbitrary nested code blocks • Local variable var reserved type (JDK10) • Lambda param var (JDK11) • Default methods in interfaces

Slide 34

Slide 34 text

Parrot: Java-style do…while // classic Java-style do..while loop def count = 5 def fact = 1 do { fact *= count-- } while(count > 1) assert fact == 120 Also: enhanced classic for loop and multi-assignment looping

Slide 35

Slide 35 text

Parrot: Java-style array initialization def primes = new int[] {2, 3, 5, 7, 11} assert primes.size() == 5 && primes.sum() == 28 assert primes.class.name == '[I' def pets = new String[] {'cat', 'dog'} assert pets.size() == 2 && pets.sum() == 'catdog' assert pets.class.name == '[Ljava.lang.String;' // traditional Groovy alternative still supported String[] groovyBooks = [ 'Groovy in Action', 'Making Java Groovy' ] assert groovyBooks.every{ it.contains('Groovy') }

Slide 36

Slide 36 text

JPMS: split package redress groovy: groovy.xml.QName groovy.namespace.QName groovy-ant: groovy.util (includes AntBuilder) groovy.ant groovy-console: groovy.ui.ConsoleApplet groovy.inspect groovy.console groovy.inspect.swingui groovy.console.ui groovy.ui groovy.console.ui groovy-groovysh: org.codehaus.groovy.tools.shell org.codehaus.groovy.groovysh.tools Deprecated Copied and original deprecated

Slide 37

Slide 37 text

JPMS: split package redress (cont’d) groovy-jmx: groovy.util.GroovyMBean groovy.jmx groovy-nio: org.codehaus.groovy.runtime.WritablePath org.apache.groovy.nio.runtime org.codehaus.groovy.runtime.NioGroovyMethods org.apache.groovy.nio.extensions.NioExtensions groovy-swing (SwingBuilder only produces new classes): org.codehaus.groovy.binding org.apache.groovy.swing.binding org.codehaus.groovy.runtime org.apache.groovy.swing.extensions groovy.model groovy.swing.model groovy.inspect.swingui org.apache.groovy.swing.table

Slide 38

Slide 38 text

JPMS: split package redress (cont’d) groovy-test: org.codehaus.groovy.runtime.ScriptTestAdapter org.apache.groovy.test groovy.transform.NotYetImplemented groovy.test groovy.util (includes GroovyTestCase) groovy.test groovy.lang groovy.test groovy-xml: groovy.util (includes XmlParser & XmlSlurper) groovy.xml org.codehaus.groovy.tools.xml.DomToGroovy org.apache.groovy.xml.tools • Migrate over lifetime of Groovy 3 with some caveats • Don’t mix and match old and new versions of classes • Deprecated classes removed in 4.0 to make jars module friendly

Slide 39

Slide 39 text

JPMS: remove illegal access warnings

Slide 40

Slide 40 text

Groovy by the numbers: Enhancements ❖ On-going innovation Remember these graphs?

Slide 41

Slide 41 text

@ASTTest @AutoClone @AutoExternalize @BaseScript @Bindable @Builder @Canonical @Category @CompileDynamic @CompileStatic @ConditionalInterrupt @Delegate @EqualsAndHashCode @ExternalizeMethods @ExternalizeVerifier @Field @Newify @NotYetImplemented @PackageScope @Singleton @Sortable @SourceURI @Synchronized @TailRecursive @ThreadInterrupt @TimedInterrupt @ToString @Trait @TupleConstructor @TypeChecked @Vetoable @WithReadLock @WithWriteLock AST Transformations – Groovy 2.4, Groovy 2.5, Groovy 3.0 @AutoFinal @AutoImplement @ImmutableBase @ImmutableOptions @MapConstructor @NamedDelegate @NamedParam @NamedParams @NamedVariant @PropertyOptions @VisibilityOptions @GroovyDoc @NullCheck * Improved in 2.5 @Grab • @GrabConfig • @GrabResolver • @GrabExclude @Grapes @Immutable @IndexedProperty @InheritConstructors @Lazy Logging: • @Commons • @Log • @Log4j • @Log4j2 • @Slf4j @ListenerList @Mixin

Slide 42

Slide 42 text

GDK improvements

Slide 43

Slide 43 text

4.0 Roadmap

Slide 44

Slide 44 text

Important naming/structuring changes Maven coordinate change org.codehaus.groovy org.apache.groovy Legacy package removal groovy-xml: groovy.util (includes XmlParser & XmlSlurper) groovy.xml Module changes for groovy-all groovy-testng: was included, now optional groovy-yaml: was optional, now included

Slide 45

Slide 45 text

Legacy consolidation Old parser removal Antlr 2 Antlr4 Classic bytecode generation removal Classic Indy

Slide 46

Slide 46 text

Built-in type checkers: regex checker import groovy.transform.TypeChecked @TypeChecked(extensions = 'groovy.typecheckers.RegexChecker') def whenIs2020Over() { def newYearsEve = '2020-12-31' def matcher = newYearsEve =~ /(\d{4})-(\d{1,2})-(\d{1,2}/ } def newYearsEve = '2020-12-31' def matcher = newYearsEve =~ /(\d{4})-(\d{1,2})-(\d{1,2}/ // PatternSyntaxException

Slide 47

Slide 47 text

Built-in type checkers: regex checker ~/\w{3/ // missing closing repetition quantifier brace ~"(.)o(.*" // missing closing group bracket Pattern.compile(/?/) // dangling meta character '?' (Java longhand) 'foobar' =~ /f[o]{2/ // missing closing repetition quantifier brace 'foobar' ==~ /(foo/ // missing closing group bracket Pattern.matches(/?/, 'foo') // dangling meta character '?' (Java longhand) def m = 'foobar' =~ /(...)(...)/ assert m[0][1] == 'foo' // okay assert m[0][3] // type error: only two groups in regex Pattern p = Pattern.compile('(...)(...)') Matcher m = p.matcher('foobar') assert m.find() assert m.group(1) == 'foo' // okay assert m.group(3) // type error: only two groups in regex

Slide 48

Slide 48 text

Built-in macro methods def num = 42 def list = [1 ,2, 3] def range = 0..5 def string = 'foo' println NV(num, list, range, string) println NVI(range) println NVD(range)

Slide 49

Slide 49 text

JavaShell import org.apache.groovy.util.JavaShell def opts = ['--enable-preview', '--release', '14'] def src = 'record Coord(int x, int y) {}' Class coordClass = new JavaShell().compile('Coord', opts, src) assert coordClass.newInstance(5, 10).toString() == 'Coord[x=5, y=10]'

Slide 50

Slide 50 text

@POJO 3 true Point(x:1, y:3) Point(x:2, y:2) Point(x:3, y:1) 2

Slide 51

Slide 51 text

Groovy 2.5: AST Transforms: @Immutable becomes meta-annotation @Immutable class Point { int x, y } @ToString(includeSuperProperties = true, cache = true) @EqualsAndHashCode(cache = true) @ImmutableBase @ImmutableOptions @PropertyOptions(propertyHandler = ImmutablePropertyHandler) @TupleConstructor(defaults = false) @MapConstructor(noArg = true, includeSuperProperties = true, includeFields = true) @KnownImmutable class Point { int x, y }

Slide 52

Slide 52 text

Groovy 4.0: AST Transforms: @RecordType meta-annotation @RecordType class Point { int x, y } @RecordBase @ToString(cache = true, includeNames = true) @EqualsAndHashCode(cache = true, useCanEqual = false) @ImmutableOptions @PropertyOptions(propertyHandler = ImmutablePropertyHandler) @TupleConstructor(defaults = false) @MapConstructor @KnownImmutable @POJO class Point { int x, y }

Slide 53

Slide 53 text

@RecordType Produces a class that: • is implicitly final • has a private final field firstName with an accessor method firstName(); ditto for lastName • has a default Cyclist(String, String) constructor • has a default serialVersionUID of 0L • has implicit toString(), equals() and hashCode() methods

Slide 54

Slide 54 text

groovy-contracts module Design-by-contract

Slide 55

Slide 55 text

Still being explored for future Groovy versions • Switch expressions • Additional destructuring/pattern matching within switch case expressions • Language integrated query • Smarter type checking: non-null, pure • Module definitions in Groovy

Slide 56

Slide 56 text

GINQ – Groovy’s language integrated query • Incubating status: feedback welcome Queryable.from([0, 1, 2]).select(n -> n) from n in [0, 1, 2] select n

Slide 57

Slide 57 text

GINQ – Groovy’s language integrated query • Incubating status: feedback welcome def nums1 = [1, 2, 3] def nums2 = [1, 2, 3] assert [[2, 1], [3, 2], [4, 3]] == GQ { from n1 in nums1 innerjoin n2 in nums2 on n1 == n2 select n1 + 1, n2 }.toList() def numbers = [0, 1, 2, 3, 4, 5] assert [2, 4, 6] == GQ { from n in numbers where n > 0 && n <= 3 select n * 2 }.toList() def persons = [new Person('Linda', 100, 'Female’), new Person('Daniel', 135, 'Male’), new Person('David', 121, 'Male')] assert [['Female', 1], ['Male', 2]] == GQ { from p in persons groupby p.gender orderby count(p.name) select p.gender, count(p.name) }.toList() from n in [0, 1, 2] select n