© Adam L. Davis 2019
Adam L. DavisLearning Groovy 3https://doi.org/10.1007/978-1-4842-5058-7_2

2. Groovy 101

Adam L. Davis1 
(1)
New York, NY, USA
 

In this chapter, we are going to cover the basics of Groovy, the history of Groovy, and the advantages of using Groovy.

What Is Groovy?

Groovy is a flexible open source language built for the JVM (Java Virtual Machine) with a Java-like syntax. It can be used dynamically (where any variable can hold any type of object) or statically typed (where the type of each variable is heavily restricted); it’s your choice. In most other languages, it is one or the other. It supports functional programming constructs, including first-class functions, currying, and more. It has multiple inheritance, type inference, and meta-programming.

Groovy began in 2003 partly as a response to Ruby. Its main features were dynamic typing, meta-programming (the ability to change classes at runtime), and tight integration with Java. Although its original creator left it around 2005, many developers1 in the community have contributed to it over the years. Various organizations have supported the development of Groovy in the past, and like many open source projects, it cannot be attributed to one person or company. It has been an Apache Software Foundation2 project since 2015.

Groovy is very similar in syntax to Java, so it is generally easy for Java developers to learn (Java code is generally valid Groovy code, especially since Groovy 3.0, which added support for Java-like array declarations and many other Java syntax features). However, Groovy has many additional features and relaxed syntax rules: closures, dynamic typing, meta-programming (via metaClass), optional semicolons, regex support, operator overloading, GStrings, and more. Groovy is interpreted at runtime, but in Groovy 2.0, the ability to compile to byte-code and enforce type-checking were added to the language.

Compact Syntax

Groovy’s syntax can be made far more compact than Java. For example, the following code in Standard Java 5+ should print out "Rod":
1   for (String it : new  String[] {"Rod", "Carlos", "Chris"})
2           if (it.length() < 4)
3                   System.out.println(it);
The same thing can be expressed in Groovy in one line as the following:
1   ["Rod", "Carlos", "Chris"].findAll{it.size() < 4}.each{println it}

It has tons of built-in features to make this possible (compact list definition, extensions to JDK objects, closures, optional semicolons, println method, and optional parentheses).

The method findAll traverses the list and uses the given test to create a new collection with only the objects that pass the test.

Dynamic def

A key feature of Groovy is dynamic typing using the def keyword. This keyword replaces any type definition, thereby allowing variables to be of any type. This is somewhat like defining variables as Object but not exactly the same because the Groovy compiler treats def differently. For example, you can use def and still use the @TypeChecked annotation, which we will cover later.

List and Map Definitions

Groovy makes list and map definitions much more concise and simple. You simply use brackets ([]) and the mapping symbol (:) for mapping keys to values:
1   def  list = [1, 2]
2   def map = [cars: 2, boats: 3]
3   println list.getClass() // java.util.ArrayList
4   println map.getClass() // java.util.LinkedHashMap
By default, Groovy interprets map key values as strings without requiring quotes. When working with maps with String keys, Groovy makes life much easier by allowing you to refer to keys using dot-notation (avoiding the get and put methods). For example:
1   map.cars = 2
2   map.boats = 3
3   map.planes = 0
4   println map.cars // 2

This even makes it possible to Mock objects using a map when testing in some cases (which is covered in a later chapter).

Groovy lists even override the left shift operator (<<), which allows the following syntax example:
1   def list = []
2   list.add(new  Vampire("Count Dracula", 1897))
3   // or
4   list << new  Vampire("Count Dracula", 1897)
5   // or
6   list += new  Vampire("Count Dracula", 1897)

../images/426440_2_En_2_Chapter/426440_2_En_2_Figa_HTML.gif Tip

Groovy allows overriding of common operators like plus and minus. We will cover this in a later chapter.

Groovy GDK

Built-in Groovy types (the GDK) are much the same as Java’s except that Groovy adds tons of methods to every class.

For example, the "each" method allows you to iterate over a collection as follows:
1   ["Java", "Groovy", "Scala"].each{ println it }

The println and print methods are shorthand for calling those methods on System.out. We will cover the GDK more in depth later.

Everything Is an Object

Unlike in Java, primitives can be used like objects at any time, so there appears to be no distinction. For example, since the GDK adds the times method to Number, you can do the following:
1   100.times { println "hi" }

This would print "hi" 100 times.

Easy Properties

Groovy takes the idea of Java Beans to a whole new level. You can get and set Bean properties using dot-notation (and Groovy automatically adds getters and setters to your classes if you don’t).

For example, instead of person.getFirstName(), you can use person.firstName. When setting properties, instead of person.setFirstName("Bob") you can just use person.firstName = 'Bob'.

../images/426440_2_En_2_Chapter/426440_2_En_2_Figb_HTML.gif Tip

Unlike Java, Groovy always defaults to public.

You can also easily get a list of all properties of an object in Groovy using .properties. For example:
1   println person.properties

../images/426440_2_En_2_Chapter/426440_2_En_2_Figc_HTML.gifTip

Use properties to explore some class in Groovy that you want to know more about.

GString

Groovy adds its own class, called GString , which allows you to embed Groovy code within strings. This is another feature that makes Groovy very concise and easy to read. A GString is created every time you use double quotes ("") in Groovy.

For example, it makes it easy to embed a bunch of variables into a string:
1   def os = 'Linux'
2   def cores = 2
3   println("Cores: $cores, OS: $os, Time: ${new Date()}")

The dollar sign $ allows you to refer directly to variables, and ${code} allows you to execute arbitrary Groovy code when included in a Gstring.

Since String is a final class in the JDK, it cannot be extended, so GString is not a subclass of String. Although it can be used in place of String in most places within Groovy code, this can cause some issues when a java.lang.String is needed.

../images/426440_2_En_2_Chapter/426440_2_En_2_Figd_HTML.gif Tip

If you just want to use a java.lang.String, you should use single quotes ('foo').

Closures

A closure is a block of code in Groovy, which may or may not take parameters and return a value. It's similar to lambda expressions in Java 8 or an inner class with one method. Closures can be extremely useful in many ways, which will be covered in subsequent chapters. For example, closures are used by the findAll, each, and times methods, as you have already seen.

Groovy closures have several implicit variables:
  • it—If the closure has one argument, it can be referred to implicitly as it.

  • this—Refers to the enclosing class.

  • owner—The same as this unless it is enclosed in another closure. For example, run the following code to see the difference:

    def print1 = {list ->
        list.each{println owner}  //prints first Closure
        println owner}       //prints the enclosing class
    print1([1,2])
  • delegate—Usually the same as owner but you can change it (this allows the methods of delegate to be in the scope of the closure).

Closures can be passed as method arguments. When this is done (and it is the last argument), the closure may go outside the parentheses. For example, when using the collect method (which uses the given closure to transform elements of list into a new list), you can call it as follows:
1   def  list = ['foo','bar']
2   def newList = []
3   list.collect( newList ) {
4     it.toUpperCase()
5   }
6   println newList //   ["FOO",    "BAR"]

../images/426440_2_En_2_Chapter/426440_2_En_2_Fige_HTML.gif Tip

The return keyword is completely optional in Groovy. A method or closure simply returns its last expression, as seen previously.

You can also assign closures to variables and call them later:
1   def  closr = {x -> x + 1}
2   println( closr(2) ) // prints 3

../images/426440_2_En_2_Chapter/426440_2_En_2_Figf_HTML.gif Exercise

Create a closure and print out the class of its delegate using getClass().

A Better Switch

Groovy’s switch statement is much like Java’s, except that it allows many more case expressions. For example, it allows strings, lists, ranges, and classtypes:
1   def x = 42
2   switch  ( x ) {
3   case "foo":
4       result = "found foo"
5           break
 6   case [4, 5, 6]:
 7       result = "4 5 or 6"
 8       break
 9   case 12..30: // Range
10       result = "12 to 30"
11       break
12   case Integer:
13       result = "was integer"
14       break
15   case Number:
16       result = "was number"
17       break
18   default:
19       result = "default"
20   }

In this case the result from this code will be “was integer”.

Meta-Programming

In Groovy, you can add methods to classes at runtime, even to core Java libraries. For example, the following code adds the upper method to the String class:
1   String.metaClass.upper = { -> toUpperCase() }
or for a single instance (str)
1 def str = "test"
2 str.metaClass.upper = { -> toUpperCase() }
The upper method would convert the String to uppercase:
1   str.upper() == str.toUpperCase()

Static Type Checking

If you add the @TypeChecked annotation to your class, it causes the compiler to enforce compile time type-checking. It will infer types for you, so your code can still be Groovy. It infers the Lowest Upper Bound (LUB) type based on your code. For example:
1   import   groovy.transform.*
2   @TypeChecked
3   class Foo {
4       int i = 42.0 // this does not compile
5   }
1   import   groovy.transform.*
2   @TypeChecked
3   class Foo {
4       int i = 42 // this works fine
5   }
6   new Foo()

../images/426440_2_En_2_Chapter/426440_2_En_2_Figg_HTML.gif Gotcha’s

Runtime meta-programming won’t always work! In other words, if you add a method using the metaClass and call it, that won’t compile.

Explicit type is needed in a closure: a.collect {String it -> it.toUpperCase()}

If you add the @CompileStatic annotation to your class or method, it causes the compiler to compile your Groovy code to Java-style byte-code. This would be useful when you have code that needs to be extremely performant (computation involving large lists of Integers, for example) or you need Java byte-code for some other reason. The generated byte-code is almost identical to compiled Java, and therefore the performance is nearly identical to Java (unlike @TypeChecked which still uses Groovy’s MOP—Meta Object Protocol). Both annotations are located in the groovy.transform package.

For example, you could use @CompileStatic on a class that calculates the Fibonacci sequence:3
 1   import   groovy.transform.*
 2   @CompileStatic
 3   class Foo {
 4      void getFibs(int count) {
 5         def list = [0, 1] // print first #count Fibonacci numbers
 6         count.times {
 7            print "${list.last()}"
 8            list << list.sum()
 9            list = list.tail()
10         }
11      }
12   }

../images/426440_2_En_2_Chapter/426440_2_En_2_Figh_HTML.gif Exercise

Try using @TypeChecked and the def keyword. It works surprisingly well.

Elvis Operator

The elvis operator was born from a common idiom in Java: using the ternary operation to provide a default value. For example:
1   String name = person.getName() == null ? "Bob" :    person.getName();
Instead in Groovy you can just use the elvis operator:
1   String name = person.getName() ?: "Bob"

Safe Dereference Operator

Similar to the elvis operator, Groovy has the safe dereference operator that allows you to easily avoid null-pointer exceptions. It involves simply adding a question mark. For example:
1   String name = person?.getName()

This would simply set name to null if person is null. It also works for method calls.

../images/426440_2_En_2_Chapter/426440_2_En_2_Figi_HTML.gif Tip

Write some Groovy code using the elvis operators and safe dereference several times until you memorize the syntax.

A Brief History

What follows is a brief history of updates to the Groovy language starting with Groovy 1.8. This will help you if you must use an older version of Groovy or if you haven’t looked at Groovy in several years.

Groovy 1.8

  • Command chains—The code "pull request on github" is executed as: pull(request).on(github)

  • GPars4 Bundled for parallel and concurrent paradigms (it was later pulled out)

  • Closure annotation parameters— @Invariant({number >= 0})

  • Closure memorization—{...}.memoize()

  • Built-in JSON support—Consuming, producing, and pretty-printing

  • New AST transformations—@Log, @Field, @AutoClone, @AutoExternalizable, ...

Groovy 1.8 further improved the ability to omit unnecessary punctuation by supporting command chains, which allow you to omit parentheses and dots for a chain of method calls.

Groovy 2.0

  • Groovy became more modular, with many parts broken out into multiple jars.

  • It added the ability to create your own module which allows you to add methods to existing classes (Extension modules5).

  • @CompileStatic: Compiles your Groovy code to byte-code.

  • @TypeChecked: Enforces compile time type-checking (as seen in the previous section).

  • Java 7 alignments: Try-with-resources, underscores in numeric literals, and invoke dynamic (optionally using the –indy command option).

  • Java 7 multi-catch: catch (Exception1 | Exception2 e) {}

The huge change in Groovy 2 was the addition of the @CompileStatic and @TypeChecked annotations, which were already covered.

Groovy 2.1

  • Full support for the JDK 7 “invoke dynamic” instruction and API.

  • @DelegatesTo—A special annotation for closure delegate based DSLs and static type-checker extensions.

  • Compile-time meta-annotations— @groovy.transform.AnnotationCollector can be used to create a meta-annotation.

  • Many improvements to existing AST transformations.

This release saw a huge improvement in performance by taking advantage of Java 7’s invoke dynamic. However, it is not enabled by default (this will be covered in a later chapter; basically you just have to “turn it on”).

Groovy 2.2

  • Implicit closure coercion.

  • @Memoized AST transformation for methods.

  • Bintray’s JCenter repository.

  • Define base script classes with an annotation.

  • New DelegatingScript base class for scripts.

  • @DelegatesTo with generic-type tokens.

  • Precompiled type-checking extensions.

The main point to notice here is implicit closure coercion, which allows you to use closures anywhere a SAM (single abstract method) interface could be used. Before this release, you needed to cast the closure explicitly.

Groovy 2.3

  • Official support for running Groovy on JDK 8

  • Traits

  • New and updated AST transformations

This release added a brand new concept (traits) and the trait keyword. We will cover them in a later chapter.

Groovy 2.4

  • Android support

  • Performance improvements and reduced byte-code

  • Traits @Self Type annotation

  • GDK improvements

  • More AST transformations

Outdoing themselves yet again, the developers behind Groovy made tons of improvements and included Android support.

Groovy 2.5

Groovy 2.5 added support for JDK9+, added 11 new AST transformations, and added the macro feature which makes writing AST transformations much easier.

The annotations added in Groovy 2.5 include @AutoFinal, @AutoImplement, @ImmutableBase, @ImmutableOptions, @MapConstructor, @NamedDelegate, @NamedParam, @NamedParams, @NamedVariant, @PropertyOptions, and @VisibilityOptions.

Some of these annotations are described as follows:
  • @AutoImplement : Automatically implements missing abstract methods (such as those from an interface). You can specify an exception to throw from those methods, such as UnsupportedOperationException. It can be useful for generating test stubs or when you only need to implement a subset of inherited abstract methods.

  • @AutoFinal : Automatically adds final modifier to method parameters.

  • @MapConstructor : Adds a constructor to your class that has one map parameter, expects field names as keys, and sets the corresponding field values. For example:

    @groovy.transform.MapConstructor
    class Person { String firstName; String lastName }
    // later on...
    def p= new Person(firstName: 'Peter', lastName: 'Quill')
    assert p.firstName == 'Peter'

Also many annotations were improved with additional attributes. For example, @TupleConstructor now includes seven more attributes. The @Immutable annotation was updated to recognize that the date/time classes added in Java 8 are immutable, and to handle Optional.

Groovy 2.6

The 2.6 version of Groovy was going to be much like Groovy 3 without the need for JDK 8, but it was retired to focus on faster delivery of Groovy 3.0.

Groovy 3.0

Groovy 3.0 sports a completely rewritten parser (nicknamed Parrot) that brings Groovy up to parity with the latest Java 11 syntax along with new Groovy-only features. It runs on JDK 8 minimum and has better support for JDK 9/10/11.

The Java-like syntax now includes Java 8 style lambda expressions and method references and some tricky syntax which has eluded Groovy for many years: array initialization, do/while loops, commas in loop declarations, and some others.

New Operators

Identity

The operator “===” can now be used to express identity-equal and !== to mean not identity-equal. Since Groovy interprets == as “.equals”, it used “.is” for identity-equals in the past. The support of “===” should avoid some confusion. This is similar to JavaScript’s === operator.

Negative variants of operators

The new operator !instanceof and !in are now supported. This will simplify the syntax in these situations. Before you would have to type !(x instanceof Date), whereas now you can simply type x !instanceof Date. Likewise, for testing negative set inclusion, what used to be !(x in [1,2,3]) can now be written as x !in [1,2,3].

Elvis assignment

You may be familiar with the elvis operator (?:) in Groovy. In many cases you would use this operation to provide a default when assigning a value. For example, name = name ?: 'None'. Now you can shorten this expression to have the same meaning in Groovy 3 with the following: name ?= 'None'

Safe indexing

Much like the safe-reference operator, there is now a safe-indexing operator, ?. This allows you to access an index of an array (or list), but if the array is null, it will return null instead of throwing an exception. For example, the following would set the value to the first value of the array, or null if the array is null: value = array?[0]

Java parity

Groovy 3 support new features added from Java 8 to 11, such as lambda expressions, method references, constructor references, try-with-resources, code blocks, non-static inner classes, and even local variables (var).

All flavors of lambda expressions are supported (and compiled to closures unless you use @CompileStatic):
  • No parameters—() -> expression

  • Single parameter—x -> expression

  • Explicit return is optional—(x, y) -> { x * y }

  • Types are allowed—(int x, int y) -> { return x + y }

  • Default values are allowed—(int x, int y = 10) -> x+y

Groovy has had method references for a long time, but Groovy 3 adds support for the Java 8+ syntax using the double colon. For example, the following is valid Groovy:
    def sq(x) { x*x }
    // var is similar to def
    var squares = (1..9).collect( this::sq ) //method ref
    //prints squares=[1, 4, 9, 16, 25, 36, 49, 64, 81]
Groovy 3 also implements the try-with-resources:
    def f = new File('temp')
    try (PrintWriter pw = new PrintWriter(f)) {
        pw.println("TEST!")
    }

This would result in the file name “temp” having the contents “TEST!” and would automatically close the PrintWriter whether or not there were errors.

Summary

In this chapter, you learned about the following:
  • How Groovy extends and simplifies programming on the JDK in a familiar syntax.

  • Groovy’s dynamic def, easy properties, closures, better “switch,” meta-programming, type-checking and static-compile annotations, the elvis operator, and safe dereference.

  • A brief overview of the features available to Groovy and in what versions they were introduced.

  • How Groovy 3.0 brings Groovy up to date with Java’s latest syntax as well as supporting some additional Java syntax that was not supported before.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset