© Adam L. Davis 2016

Adam L. Davis, Learning Groovy, 10.1007/978-1-4842-2117-4_2

2. Groovy 101

Adam L. Davis

(1)New York, 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 developer abandoned it, 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 is now an Apache Software Foundation2 project .

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). However, Groovy has many additional features and relaxed syntax rules: closures, dynamic typing, meta-programming (via metaClass), semicolons are optional, 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, the 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.

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)
A426440_1_En_2_Figa_HTML.jpg 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'.

A426440_1_En_2_Figa_HTML.jpg 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
A426440_1_En_2_Figb_HTML.jpg Tip

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

GString

Groovy adds its own class, called GString, that allows you to embed Groovy code within strings. This is another features 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 $ allows you to refer directly to variables, and ${code} allows you to execute arbitrary Groovy code when included in a GString.

A426440_1_En_2_Figa_HTML.jpg 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 later. 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.

  • delegate—Usually the same as owner but you can change it (this allows the methods of delegate to be in scope).

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"]
A426440_1_En_2_Figa_HTML.jpg 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
A426440_1_En_2_Figb_HTML.jpg Tip

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 class types:

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   }

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()
A426440_1_En_2_Figc_HTML.jpg Gotcha’s
  • Runtime meta-programming won't work!

  • 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 byte-code. This would be useful when you have code that needs to be extremely performant or you need Java byte-code for some other reason. The generated byte-code is almost identical to compiled Java. Both annotations are located in the groovy.transform package . For example:

 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   }
A426440_1_En_2_Figb_HTML.jpg Tip

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 consists of 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.

A426440_1_En_2_Figb_HTML.jpg 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: pull request on github => pull(request).on(github)

  • GPars3 Bundled for parallel and concurrent paradigms

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

  • Closure memoization: {...}.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

  • More modular - multiple jars: Create your own module (Extension modules4)

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

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

  • Java 7 alignments: Project coin and invoke dynamic

  • Java 7 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

  • Groovy 2.1's distribution bundles the concurrency library GPars 1.0

  • @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

  • New @Log variant for the Log4j2 logging framework

  • @DelegatesTo with generics 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.

Summary

In this chapter, you learned about:

  • How Groovy extends and simplifies programming on the JDK in a familiar syntax.

  • Groovy’s dynamic def, easy properties, closures, a 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.

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

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