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
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
This even makes it possible to Mock objects using a map when testing in some cases (which is covered in a later chapter).
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.
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
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'.
Tip
Unlike Java, Groovy always defaults to public.
Tip
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.
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.
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.
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 Closureprintln owner} //prints the enclosing classprint1([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).
Tip
The return keyword is completely optional in Groovy. A method or closure simply returns its last expression, as seen previously.
Exercise
Create a closure and print out the class of its delegate using getClass().
A Better Switch
In this case the result from this code will be “was integer”.
Meta-Programming
Static Type Checking
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.
Exercise
Try using @TypeChecked and the def keyword. It works surprisingly well.
Elvis Operator
Safe Dereference Operator
This would simply set name to null if person is null. It also works for method calls.
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.
@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.MapConstructorclass 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).
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
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
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.