Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Groovy day training

paulking
November 12, 2020

Groovy day training

Slide material for a "Groovy day of training" workshop.

paulking

November 12, 2020
Tweet

More Decks by paulking

Other Decks in Programming

Transcript

  1. 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
  2. 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
  3. 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
  4. History: Why Groovy? • Java perceived as good for core

    library development • But there were some pain points © ASERT 2006-2020 Java
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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)
  14. 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
  15. 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
  16. 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
  17. // 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
  18. New operators: Safe navigation (Groovy 1+) def (a, b) =

    ['foo', null] assert a?.size() == 3 assert b?.size() == null // but no NPE thrown
  19. 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
  20. 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
  21. 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]
  22. 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())
  23. 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()
  24. Method closures & method references @groovy.transform.CompileStatic def method() { Function<String,

    String> 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
  25. 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<Integer> thrice = (final var x) -> x * three assert 6 == thrice.apply(two) assert 9 == thrice(three)
  26. 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)
  27. Parrot: Java-style Lambdas and streams var three = 3 IntFunction<Integer>

    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()
  28. 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
  29. 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
  30. 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') }
  31. 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
  32. 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
  33. 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
  34. @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
  35. 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
  36. 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
  37. 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
  38. 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)
  39. 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]'
  40. 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 }
  41. 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 }
  42. @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
  43. 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
  44. 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
  45. 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