Movatterモバイル変換


[0]ホーム

URL:


Groovy Language Documentation

version 4.0.27
Table of Contents

Introduction

Groovy…​

  • is an agile and dynamic language for the Java Virtual Machine

  • builds upon the strengths of Java but has additional power features inspired by languages like Python, Ruby and Smalltalk

  • makes modern programming features available to Java developers with almost-zero learning curve

  • provides the ability to statically type check and statically compile your code for robustness and performance

  • supports Domain-Specific Languages and other compact syntax so your code becomes easy to read and maintain

  • makes writing shell and build scripts easy with its powerful processing primitives, OO abilities and an Ant DSL

  • increases developer productivity by reducing scaffolding code when developing web, GUI, database or console applications

  • simplifies testing by supporting unit testing and mocking out-of-the-box

  • seamlessly integrates with all existing Java classes and libraries

  • compiles straight to Java bytecode so you can use it anywhere you can use Java

1. Groovy Language Specification

1.1. Syntax

This chapter covers the syntax of the Groovy programming language.The grammar of the language derives from the Java grammar,but enhances it with specific constructs for Groovy, and allows certain simplifications.

1.1.1. Comments

Single-line comment

Single-line comments start with// and can be found at any position in the line.The characters following//, until the end of the line, are considered part of the comment.

// a standalone single line commentprintln "hello" // a comment till the end of the line
Multiline comment

A multiline comment starts with/* and can be found at any position in the line.The characters following/* will be considered part of the comment, including new line characters,up to the first*/ closing the comment.Multiline comments can thus be put at the end of a statement, or even inside a statement.

/* a standalone multiline comment   spanning two lines */println "hello" /* a multiline comment starting                   at the end of a statement */println 1 /* one */ + 2 /* two */
Groovydoc comment

Similarly to multiline comments, Groovydoc comments are multiline, but start with/** and end with*/.Lines following the first Groovydoc comment line can optionally start with a star*.Those comments are associated with:

  • type definitions (classes, interfaces, enums, annotations),

  • fields and properties definitions

  • methods definitions

Although the compiler will not complain about Groovydoc comments not being associated with the above language elements,you should prepend those constructs with the comment right before it.

/** * A Class description */class Person {    /** the name of the person */    String name    /**     * Creates a greeting method for a certain person.     *     * @param otherPerson the person to greet     * @return a greeting message     */    String greet(String otherPerson) {       "Hello ${otherPerson}"    }}

Groovydoc follows the same conventions as Java’s own Javadoc.So you’ll be able to use the same tags as with Javadoc.

In addition, Groovy supportsRuntime Groovydoc since 3.0.0, i.e. Groovydoc can be retained at runtime.

Runtime Groovydoc is disabled by default. It can be enabled by adding JVM option-Dgroovy.attach.runtime.groovydoc=true

The Runtime Groovydoc starts with/**@ and ends with*/, for example:

/**@ * Some class groovydoc for Foo */class Foo {    /**@     * Some method groovydoc for bar     */    void bar() {    }}assert Foo.class.groovydoc.content.contains('Some class groovydoc for Foo')(1)assert Foo.class.getMethod('bar', new Class[0]).groovydoc.content.contains('Some method groovydoc for bar')(2)
1Get the runtime groovydoc for classFoo
2Get the runtime groovydoc for methodbar
Shebang line

Beside the single-line comment, there is a special line comment, often called theshebang line understood by UNIX systemswhich allows scripts to be run directly from the command-line, provided you have installed the Groovy distributionand thegroovy command is available on thePATH.

#!/usr/bin/env groovyprintln "Hello from the shebang line"
The# character must be the first character of the file. Any indentation would yield a compilation error.

1.1.2. Keywords

Groovy has the following reserved keywords:

Table 1. Reserved Keywords

abstract

assert

break

case

catch

class

const

continue

def

default

do

else

enum

extends

final

finally

for

goto

if

implements

import

instanceof

interface

native

new

null

non-sealed

package

public

protected

private

return

static

strictfp

super

switch

synchronized

this

threadsafe

throw

throws

transient

try

while

Of these,const,goto,strictfp, andthreadsafe are not currently in use.

The reserved keywords can’t in general be used for variable, field and method names.

A trick allows methods to be defined having the same name as a keywordby surrounding the name in quotes as shown in the following example:

// reserved keywords can be used for method names if quoteddef "abstract"() { true }// when calling such methods, the name must be qualified using "this."this.abstract()

Using such names might be confusing and is often best to avoid.The trick is primarily intended to enable certain Java integration scenariosand certainDSL scenarios wherehaving "verbs" and "nouns" with the same name as keywords may be desirable.

In addition, Groovy has the following contextual keywords:

Table 2. Contextual Keywords

as

in

permits

record

sealed

trait

var

yields

These words are only keywords in certain contexts and can be more freely used in some places,in particular for variables, fields and method names.

This extra lenience allows using method or variable names that were not keywords in earlierversions of Groovy or are not keywords in Java. Examples are shown here:

// contextual keywords can be used for field and variable namesdef as = trueassert as// contextual keywords can be used for method namesdef in() { true }// when calling such methods, the name only needs to be qualified using "this." in scenarios which would be ambiguousthis.in()

Groovy programmers familiar with these contextual keywords may still wish to avoidusing those names unless there is a good reason to use such a name.

The restrictions on reserved keywords also apply for theprimitive types, the boolean literals and the null literal (all of which are discussed later):

Table 3. Other reserved words

null

true

false

boolean

char

byte

short

int

long

float

double

While not recommended, the same trick as for reserved keywords can be used:

def "null"() { true }  // not recommended; potentially confusingassert this.null()     // must be qualified

Using such words as method names is potentially confusing and is often best to avoid, however,it might be useful for certain kinds ofDSLs.

1.1.3. Identifiers

Normal identifiers

Identifiers start with a letter, a dollar or an underscore.They cannot start with a number.

A letter can be in the following ranges:

  • 'a' to 'z' (lowercase ascii letter)

  • 'A' to 'Z' (uppercase ascii letter)

  • '\u00C0' to '\u00D6'

  • '\u00D8' to '\u00F6'

  • '\u00F8' to '\u00FF'

  • '\u0100' to '\uFFFE'

Then following characters can contain letters and numbers.

Here are a few examples of valid identifiers (here, variable names):

def namedef item3def with_underscoredef $dollarStart

But the following ones are invalid identifiers:

def 3tierdef a+bdef a#b

All keywords are also valid identifiers when following a dot:

foo.asfoo.assertfoo.breakfoo.casefoo.catch
Quoted identifiers

Quoted identifiers appear after the dot of a dotted expression.For instance, thename part of theperson.name expression can be quoted withperson."name" orperson.'name'.This is particularly interesting when certain identifiers contain illegal characters that are forbidden by the Java Language Specification,but which are allowed by Groovy when quoted. For example, characters like a dash, a space, an exclamation mark, etc.

def map = [:]map."an identifier with a space and double quotes" = "ALLOWED"map.'with-dash-signs-and-single-quotes' = "ALLOWED"assert map."an identifier with a space and double quotes" == "ALLOWED"assert map.'with-dash-signs-and-single-quotes' == "ALLOWED"

As we shall see in thefollowing section on strings, Groovy provides different string literals.All kind of strings are actually allowed after the dot:

map.'single quote'map."double quote"map.'''triple single quote'''map."""triple double quote"""map./slashy string/map.$/dollar slashy string/$

There’s a difference between plain character strings and Groovy’s GStrings (interpolated strings),as in that the latter case, the interpolated values are inserted in the final string for evaluating the whole identifier:

def firstname = "Homer"map."Simpson-${firstname}" = "Homer Simpson"assert map.'Simpson-Homer' == "Homer Simpson"

1.1.4. Strings

Text literals are represented in the form of chain of characters called strings.Groovy lets you instantiatejava.lang.String objects, as well as GStrings (groovy.lang.GString)which are also calledinterpolated strings in other programming languages.

Single-quoted string

Single-quoted strings are a series of characters surrounded by single quotes:

'a single-quoted string'
Single-quoted strings are plainjava.lang.String and don’t support interpolation.
String concatenation

All the Groovy strings can be concatenated with the+ operator:

assert 'ab' == 'a' + 'b'
Triple-single-quoted string

Triple-single-quoted strings are a series of characters surrounded by triplets of single quotes:

'''a triple-single-quoted string'''
Triple-single-quoted strings are plainjava.lang.String and don’t support interpolation.

Triple-single-quoted strings may span multiple lines.The content of the string can cross line boundaries without the need to split the string in several piecesand without concatenation or newline escape characters:

def aMultilineString = '''line oneline twoline three'''

If your code is indented, for example in the body of the method of a class, your string will contain the whitespace of the indentation.The Groovy Development Kit contains methods for stripping out the indentation with theString#stripIndent() method,and with theString#stripMargin() method that takes a delimiter character to identify the text to remove from the beginning of a string.

When creating a string as follows:

def startingAndEndingWithANewline = '''line oneline twoline three'''

You will notice that the resulting string contains a newline character as first character.It is possible to strip that character by escaping the newline with a backslash:

def strippedFirstNewline = '''\line oneline twoline three'''assert !strippedFirstNewline.startsWith('\n')
Escaping special characters

You can escape single quotes with the backslash character to avoid terminating the string literal:

'an escaped single quote: \' needs a backslash'

And you can escape the escape character itself with a double backslash:

'an escaped escape character: \\ needs a double backslash'

Some special characters also use the backslash as escape character:

Escape sequenceCharacter

\b

backspace

\f

formfeed

\n

newline

\r

carriage return

\s

single space

\t

tabulation

\\

backslash

\'

single quote within a single-quoted string (and optional for triple-single-quoted and double-quoted strings)

\"

double quote within a double-quoted string (and optional for triple-double-quoted and single-quoted strings)

We’ll see some more escaping details when it comes to other types of strings discussed later.

Unicode escape sequence

For characters that are not present on your keyboard, you can use unicode escape sequences:a backslash, followed by 'u', then 4 hexadecimal digits.

For example, the Euro currency symbol can be represented with:

'The Euro currency symbol: \u20AC'
Double-quoted string

Double-quoted strings are a series of characters surrounded by double quotes:

"a double-quoted string"
Double-quoted strings are plainjava.lang.String if there’s no interpolated expression,but aregroovy.lang.GString instances if interpolation is present.
To escape a double quote, you can use the backslash character: "A double quote: \"".
String interpolation

Any Groovy expression can be interpolated in all string literals, apart from single and triple-single-quoted strings.Interpolation is the act of replacing a placeholder in the string with its value upon evaluation of the string.The placeholder expressions are surrounded by${}. The curly braces may be omitted for unambiguous dotted expressions,i.e. we can use just a $ prefix in those cases.If the GString is ever passed to a method taking a String, the expression value inside the placeholderis evaluated to its string representation (by callingtoString() on that expression) and the resultingString is passed to the method.

Here, we have a string with a placeholder referencing a local variable:

def name = 'Guillaume' // a plain stringdef greeting = "Hello ${name}"assert greeting.toString() == 'Hello Guillaume'

Any Groovy expression is valid, as we can see in this example with an arithmetic expression:

def sum = "The sum of 2 and 3 equals ${2 + 3}"assert sum.toString() == 'The sum of 2 and 3 equals 5'
Not only are expressions allowed in between the${} placeholder, but so are statements. However, a statement’s value is justnull.So if several statements are inserted in that placeholder, the last one should somehow return a meaningful value to be inserted.For instance, "The sum of 1 and 2 is equal to ${def a = 1; def b = 2; a + b}" is supported and works as expected but a good practice is usually to stick to simple expressions inside GString placeholders.

In addition to${} placeholders, we can also use a lone$ sign prefixing a dotted expression:

def person = [name: 'Guillaume', age: 36]assert "$person.name is $person.age years old" == 'Guillaume is 36 years old'

But only dotted expressions of the forma.b,a.b.c, etc, are valid. Expressions containing parentheses like method calls,curly braces for closures, dots which aren’t part of a property expression or arithmetic operators would be invalid.Given the following variable definition of a number:

def number = 3.14

The following statement will throw agroovy.lang.MissingPropertyException because Groovy believes you’re trying to access thetoString property of that number, which doesn’t exist:

shouldFail(MissingPropertyException) {    println "$number.toString()"}
You can think of"$number.toString()" as being interpreted by the parser as"${number.toString}()".

Similarly, if the expression is ambiguous, you need to keep the curly braces:

String thing = 'treasure'assert 'The x-coordinate of the treasure is represented by treasure.x' ==    "The x-coordinate of the $thing is represented by $thing.x"   // <= Not allowed: ambiguous!!assert 'The x-coordinate of the treasure is represented by treasure.x' ==        "The x-coordinate of the $thing is represented by ${thing}.x"  // <= Curly braces required

If you need to escape the$ or${} placeholders in a GString so they appear as is without interpolation,you just need to use a\ backslash character to escape the dollar sign:

assert '$5' == "\$5"assert '${name}' == "\${name}"
Special case of interpolating closure expressions

So far, we’ve seen we could interpolate arbitrary expressions inside the${} placeholder, but there is a special case and notation for closure expressions. When the placeholder contains an arrow,${→}, the expression is actually a closure expression — you can think of it as a closure with a dollar prepended in front of it:

def sParameterLessClosure = "1 + 2 == ${-> 3}"(1)assert sParameterLessClosure == '1 + 2 == 3'def sOneParamClosure = "1 + 2 == ${ w -> w << 3}"(2)assert sOneParamClosure == '1 + 2 == 3'
1The closure is a parameterless closure which doesn’t take arguments.
2Here, the closure takes a singlejava.io.StringWriter argument, to which you can append content with the<< leftShift operator.In either case, both placeholders are embedded closures.

In appearance, it looks like a more verbose way of defining expressions to be interpolated,but closures have an interesting advantage over mere expressions: lazy evaluation.

Let’s consider the following sample:

def number = 1(1)def eagerGString = "value == ${number}"def lazyGString = "value == ${ -> number }"assert eagerGString == "value == 1"(2)assert lazyGString ==  "value == 1"(3)number = 2(4)assert eagerGString == "value == 1"(5)assert lazyGString ==  "value == 2"(6)
1We define anumber variable containing1 that we then interpolate within two GStrings,as an expression ineagerGString and as a closure inlazyGString.
2We expect the resulting string to contain the same string value of 1 foreagerGString.
3Similarly forlazyGString
4Then we change the value of the variable to a new number
5With a plain interpolated expression, the value was actually bound at the time of creation of the GString.
6But with a closure expression, the closure is called upon each coercion of the GString into String,resulting in an updated string containing the new number value.
An embedded closure expression taking more than one parameter will generate an exception at runtime.Only closures with zero or one parameter are allowed.
Interoperability with Java

When a method (whether implemented in Java or Groovy) expects ajava.lang.String,but we pass agroovy.lang.GString instance,thetoString() method of the GString is automatically and transparently called.

String takeString(String message) {(4)    assert message instanceof String(5)    return message}def message = "The message is ${'hello'}"(1)assert message instanceof GString(2)def result = takeString(message)(3)assert result instanceof Stringassert result == 'The message is hello'
1We create a GString variable
2We double-check it’s an instance of the GString
3We then pass that GString to a method taking a String as parameter
4The signature of thetakeString() method explicitly says its sole parameter is a String
5We also verify that the parameter is indeed a String and not a GString.
GString and String hashCodes

Although interpolated strings can be used in lieu of plain Java strings,they differ with strings in a particular way: their hashCodes are different.Plain Java strings are immutable, whereas the resulting String representation of a GString can vary,depending on its interpolated values.Even for the same resulting string, GStrings and Strings don’t have the same hashCode.

assert "one: ${1}".hashCode() != "one: 1".hashCode()

GString and Strings having different hashCode values, using GString as Map keys should be avoided,especially if we try to retrieve an associated value with a String instead of a GString.

def key = "a"def m = ["${key}": "letter ${key}"](1)assert m["a"] == null(2)
1The map is created with an initial pair whose key is a GString
2When we try to fetch the value with a String key, we will not find it, as Strings and GString have different hashCode values
Triple-double-quoted string

Triple-double-quoted strings behave like double-quoted strings, with the addition that they are multiline, like the triple-single-quoted strings.

def name = 'Groovy'def template = """    Dear Mr ${name},    You're the winner of the lottery!    Yours sincerly,    Dave"""assert template.toString().contains('Groovy')
Neither double quotes nor single quotes need be escaped in triple-double-quoted strings.
Slashy string

Beyond the usual quoted strings, Groovy offers slashy strings, which use/ as the opening and closing delimiter.Slashy strings are particularly useful for defining regular expressions and patterns,as there is no need to escape backslashes.

Example of a slashy string:

def fooPattern = /.*foo.*/assert fooPattern == '.*foo.*'

Only forward slashes need to be escaped with a backslash:

def escapeSlash = /The character \/ is a forward slash/assert escapeSlash == 'The character / is a forward slash'

Slashy strings are multiline:

def multilineSlashy = /one    two    three/assert multilineSlashy.contains('\n')

Slashy strings can be thought of as just another way to define a GString but with different escaping rules. They hence support interpolation:

def color = 'blue'def interpolatedSlashy = /a ${color} car/assert interpolatedSlashy == 'a blue car'
Special cases

An empty slashy string cannot be represented with a double forward slash, as it’s understood by the Groovy parser as a line comment.That’s why the following assert would actually not compile as it would look like a non-terminated statement:

assert '' == //

As slashy strings were mostly designed to make regexp easier so a few things thatare errors in GStrings like$() or$5 will work with slashy strings.

Remember that escaping backslashes is not required. An alternative way of thinking of this isthat in fact escaping is not supported. The slashy string/\t/ won’t contain a tab but insteada backslash followed by the character 't'. Escaping is only allowed for the slash character, i.e./\/folder/will be a slashy string containing'/folder'. A consequence of slash escaping is that a slashy stringcan’t end with a backslash. Otherwise that will escape the slashy string terminator.You can instead use a special trick,/ends with slash ${'\'}/. But best just avoid using a slashy string in such a case.

Dollar slashy string

Dollar slashy strings are multiline GStrings delimited with an opening$/ and a closing/$.The escaping character is the dollar sign, and it can escape another dollar, or a forward slash.Escaping for the dollar and forward slash characters is only needed where conflicts arise withthe special use of those characters. The characters$foo would normally indicate a GStringplaceholder, so those four characters can be entered into a dollar slashy string by escaping the dollar, i.e.$$foo.Similarly, you will need to escape a dollar slashy closing delimiter if you want it to appear in your string.

Here are a few examples:

def name = "Guillaume"def date = "April, 1st"def dollarSlashy = $/    Hello $name,    today we're ${date}.    $ dollar sign    $$ escaped dollar sign    \ backslash    / forward slash    $/ escaped forward slash    $$$/ escaped opening dollar slashy    $/$$ escaped closing dollar slashy/$assert [    'Guillaume',    'April, 1st',    '$ dollar sign',    '$ escaped dollar sign',    '\\ backslash',    '/ forward slash',    '/ escaped forward slash',    '$/ escaped opening dollar slashy',    '/$ escaped closing dollar slashy'].every { dollarSlashy.contains(it) }

It was created to overcome some of the limitations of the slashy string escaping rules.Use it when its escaping rules suit your string contents (typically if it has some slashes you don’t want to escape).

String summary table

String name

String syntax

Interpolated

Multiline

Escape character

Single-quoted

'…​'

\

Triple-single-quoted

'''…​'''

\

Double-quoted

"…​"

\

Triple-double-quoted

"""…​"""

\

Slashy

/…​/

\

Dollar slashy

$/…​/$

$

Characters

Unlike Java, Groovy doesn’t have an explicit character literal.However, you can be explicit about making a Groovy string an actual character, by three different means:

char c1 = 'A'(1)assert c1 instanceof Characterdef c2 = 'B' as char(2)assert c2 instanceof Characterdef c3 = (char)'C'(3)assert c3 instanceof Character
1by being explicit when declaring a variable holding the character by specifying thechar type
2by using type coercion with theas operator
3by using a cast to char operation
The first option1 is interesting when the character is held in a variable,while the other two (2 and3) are more interesting when a char value must be passed as argument of a method call.

1.1.5. Numbers

Groovy supports different kinds of integral literals and decimal literals, backed by the usualNumber types of Java.

Integral literals

The integral literal types are the same as in Java:

  • byte

  • char

  • short

  • int

  • long

  • java.math.BigInteger

You can create integral numbers of those types with the following declarations:

// primitive typesbyte  b = 1char  c = 2short s = 3int   i = 4long  l = 5// infinite precisionBigInteger bi =  6

If you use optional typing by using thedef keyword, the type of the integral number will vary:it’ll adapt to the capacity of the type that can hold that number.

For positive numbers:

def a = 1assert a instanceof Integer// Integer.MAX_VALUEdef b = 2147483647assert b instanceof Integer// Integer.MAX_VALUE + 1def c = 2147483648assert c instanceof Long// Long.MAX_VALUEdef d = 9223372036854775807assert d instanceof Long// Long.MAX_VALUE + 1def e = 9223372036854775808assert e instanceof BigInteger

As well as for negative numbers:

def na = -1assert na instanceof Integer// Integer.MIN_VALUEdef nb = -2147483648assert nb instanceof Integer// Integer.MIN_VALUE - 1def nc = -2147483649assert nc instanceof Long// Long.MIN_VALUEdef nd = -9223372036854775808assert nd instanceof Long// Long.MIN_VALUE - 1def ne = -9223372036854775809assert ne instanceof BigInteger
Alternative non-base 10 representations

Numbers can also be represented in binary, octal, hexadecimal and decimal bases.

Binary literal

Binary numbers start with a0b prefix:

int xInt = 0b10101111assert xInt == 175short xShort = 0b11001001assert xShort == 201 as shortbyte xByte = 0b11assert xByte == 3 as bytelong xLong = 0b101101101101assert xLong == 2925lBigInteger xBigInteger = 0b111100100001assert xBigInteger == 3873gint xNegativeInt = -0b10101111assert xNegativeInt == -175
Octal literal

Octal numbers are specified in the typical format of0 followed by octal digits.

int xInt = 077assert xInt == 63short xShort = 011assert xShort == 9 as shortbyte xByte = 032assert xByte == 26 as bytelong xLong = 0246assert xLong == 166lBigInteger xBigInteger = 01111assert xBigInteger == 585gint xNegativeInt = -077assert xNegativeInt == -63
Hexadecimal literal

Hexadecimal numbers are specified in the typical format of0x followed by hex digits.

int xInt = 0x77assert xInt == 119short xShort = 0xaaassert xShort == 170 as shortbyte xByte = 0x3aassert xByte == 58 as bytelong xLong = 0xffffassert xLong == 65535lBigInteger xBigInteger = 0xaaaaassert xBigInteger == 43690gDouble xDouble = new Double('0x1.0p0')assert xDouble == 1.0dint xNegativeInt = -0x77assert xNegativeInt == -119
Decimal literals

The decimal literal types are the same as in Java:

  • float

  • double

  • java.math.BigDecimal

You can create decimal numbers of those types with the following declarations:

// primitive typesfloat  f = 1.234double d = 2.345// infinite precisionBigDecimal bd =  3.456

Decimals can use exponents, with thee orE exponent letter, followed by an optional sign,and an integral number representing the exponent:

assert 1e3  ==  1_000.0assert 2E4  == 20_000.0assert 3e+1 ==     30.0assert 4E-2 ==      0.04assert 5e-1 ==      0.5

Conveniently for exact decimal number calculations, Groovy choosesjava.math.BigDecimal as its decimal number type.In addition, bothfloat anddouble are supported, but require an explicit type declaration, type coercion or suffix.Even ifBigDecimal is the default for decimal numbers, such literals are accepted in methods or closures takingfloat ordouble as parameter types.

Decimal numbers can’t be represented using a binary, octal or hexadecimal representation.
Underscore in literals

When writing long literal numbers, it’s harder on the eye to figure out how some numbers are grouped together, for example with groups of thousands, of words, etc. By allowing you to place underscore in number literals, it’s easier to spot those groups:

long creditCardNumber = 1234_5678_9012_3456Llong socialSecurityNumbers = 999_99_9999Ldouble monetaryAmount = 12_345_132.12long hexBytes = 0xFF_EC_DE_5Elong hexWords = 0xFFEC_DE5Elong maxLong = 0x7fff_ffff_ffff_ffffLlong alsoMaxLong = 9_223_372_036_854_775_807Llong bytes = 0b11010010_01101001_10010100_10010010
Number type suffixes

We can force a number (including binary, octals and hexadecimals) to have a specific type by giving a suffix (see table below), either uppercase or lowercase.

TypeSuffix

BigInteger

G org

Long

L orl

Integer

I ori

BigDecimal

G org

Double

D ord

Float

F orf

Examples:

assert 42I == Integer.valueOf('42')assert 42i == Integer.valueOf('42') // lowercase i more readableassert 123L == Long.valueOf("123") // uppercase L more readableassert 2147483648 == Long.valueOf('2147483648') // Long type used, value too large for an Integerassert 456G == new BigInteger('456')assert 456g == new BigInteger('456')assert 123.45 == new BigDecimal('123.45') // default BigDecimal type usedassert .321 == new BigDecimal('.321')assert 1.200065D == Double.valueOf('1.200065')assert 1.234F == Float.valueOf('1.234')assert 1.23E23D == Double.valueOf('1.23E23')assert 0b1111L.class == Long // binaryassert 0xFFi.class == Integer // hexadecimalassert 034G.class == BigInteger // octal
Math operations

Althoughoperators are covered in more detail elsewhere, it’s important to discuss the behavior of math operationsand what their resulting types are.

Division and power binary operations aside (covered below),

  • binary operations betweenbyte,char,short andint result inint

  • binary operations involvinglong withbyte,char,short andint result inlong

  • binary operations involvingBigInteger and any other integral type result inBigInteger

  • binary operations involvingBigDecimal withbyte,char,short,int andBigInteger result inBigDecimal

  • binary operations betweenfloat,double andBigDecimal result indouble

  • binary operations between twoBigDecimal result inBigDecimal

The following table summarizes those rules:

bytecharshortintlongBigIntegerfloatdoubleBigDecimal

byte

int

int

int

int

long

BigInteger

double

double

BigDecimal

char

int

int

int

long

BigInteger

double

double

BigDecimal

short

int

int

long

BigInteger

double

double

BigDecimal

int

int

long

BigInteger

double

double

BigDecimal

long

long

BigInteger

double

double

BigDecimal

BigInteger

BigInteger

double

double

BigDecimal

float

double

double

double

double

double

double

BigDecimal

BigDecimal

Thanks to Groovy’s operator overloading, the usual arithmetic operators work as well withBigInteger andBigDecimal,unlike in Java where you have to use explicit methods for operating on those numbers.
The case of the division operator

The division operators/ (and/= for division and assignment) produce adouble resultif either operand is afloat ordouble, and aBigDecimal result otherwise(when both operands are any combination of an integral typeshort,char,byte,int,long,BigInteger orBigDecimal).

BigDecimal division is performed with thedivide() method if the division is exact(i.e. yielding a result that can be represented within the bounds of the same precision and scale),or using aMathContext with aprecisionof the maximum of the two operands' precision plus an extra precision of 10,and ascaleof the maximum of 10 and the maximum of the operands' scale.

For integer division like in Java, you should use theintdiv() method,as Groovy doesn’t provide a dedicated integer division operator symbol.
The case of the power operator

The power operation is represented by the** operator, with two parameters: the base and the exponent.The result of the power operation depends on its operands, and the result of the operation(in particular if the result can be represented as an integral value).

The following rules are used by Groovy’s power operation to determine the resulting type:

  • If the exponent is a decimal value

    • if the result can be represented as anInteger, then return anInteger

    • else if the result can be represented as aLong, then return aLong

    • otherwise return aDouble

  • If the exponent is an integral value

    • if the exponent is strictly negative, then return anInteger,Long orDouble if the result value fits in that type

    • if the exponent is positive or zero

      • if the base is aBigDecimal, then return aBigDecimal result value

      • if the base is aBigInteger, then return aBigInteger result value

      • if the base is anInteger, then return anInteger if the result value fits in it, otherwise aBigInteger

      • if the base is aLong, then return aLong if the result value fits in it, otherwise aBigInteger

We can illustrate those rules with a few examples:

// base and exponent are ints and the result can be represented by an Integerassert    2    **   3    instanceof Integer    //  8assert   10    **   9    instanceof Integer    //  1_000_000_000// the base is a long, so fit the result in a Long// (although it could have fit in an Integer)assert    5L   **   2    instanceof Long       //  25// the result can't be represented as an Integer or Long, so return a BigIntegerassert  100    **  10    instanceof BigInteger //  10e20assert 1234    ** 123    instanceof BigInteger //  170515806212727042875...// the base is a BigDecimal and the exponent a negative int// but the result can be represented as an Integerassert    0.5  **  -2    instanceof Integer    //  4// the base is an int, and the exponent a negative float// but again, the result can be represented as an Integerassert    1    **  -0.3f instanceof Integer    //  1// the base is an int, and the exponent a negative int// but the result will be calculated as a Double// (both base and exponent are actually converted to doubles)assert   10    **  -1    instanceof Double     //  0.1// the base is a BigDecimal, and the exponent is an int, so return a BigDecimalassert    1.2  **  10    instanceof BigDecimal //  6.1917364224// the base is a float or double, and the exponent is an int// but the result can only be represented as a Double valueassert    3.4f **   5    instanceof Double     //  454.35430372146965assert    5.6d **   2    instanceof Double     //  31.359999999999996// the exponent is a decimal value// and the result can only be represented as a Double valueassert    7.8  **   1.9  instanceof Double     //  49.542708423868476assert    2    **   0.1f instanceof Double     //  1.0717734636432956

1.1.6. Booleans

Boolean is a special data type that is used to represent truth values:true andfalse.Use this data type for simple flags that track true/falseconditions.

Boolean values can be stored in variables, assigned into fields, just like any other data type:

def myBooleanVariable = trueboolean untypedBooleanVar = falsebooleanField = true

true andfalse are the only two primitive boolean values.But more complex boolean expressions can be represented usinglogical operators.

In addition, Groovy hasspecial rules (often referred to asGroovy Truth)for coercing non-boolean objects to a boolean value.

1.1.7. Lists

Groovy uses a comma-separated list of values, surrounded by square brackets, to denote lists.Groovy lists are plain JDKjava.util.List, as Groovy doesn’t define its own collection classes.The concrete list implementation used when defining list literals arejava.util.ArrayList by default,unless you decide to specify otherwise, as we shall see later on.

def numbers = [1, 2, 3](1)assert numbers instanceof List(2)assert numbers.size() == 3(3)
1We define a list numbers delimited by commas and surrounded by square brackets, and we assign that list into a variable
2The list is an instance of Java’sjava.util.List interface
3The size of the list can be queried with thesize() method, and shows our list contains 3 elements

In the above example, we used a homogeneous list, but you can also create lists containing values of heterogeneous types:

def heterogeneous = [1, "a", true](1)
1Our list here contains a number, a string and a boolean value

We mentioned that by default, list literals are actually instances ofjava.util.ArrayList,but it is possible to use a different backing type for our lists,thanks to using type coercion with theas operator, or with explicit type declaration for your variables:

def arrayList = [1, 2, 3]assert arrayList instanceof java.util.ArrayListdef linkedList = [2, 3, 4] as LinkedList(1)assert linkedList instanceof java.util.LinkedListLinkedList otherLinked = [3, 4, 5](2)assert otherLinked instanceof java.util.LinkedList
1We use coercion with theas operator to explicitly request ajava.util.LinkedList implementation
2We can say that the variable holding the list literal is of typejava.util.LinkedList

You can access elements of the list with the[] subscript operator (both for reading and setting values)with positive indices or negative indices to access elements from the end of the list, as well as with ranges,and use the<< leftShift operator to append elements to a list:

def letters = ['a', 'b', 'c', 'd']assert letters[0] == 'a'(1)assert letters[1] == 'b'assert letters[-1] == 'd'(2)assert letters[-2] == 'c'letters[2] = 'C'(3)assert letters[2] == 'C'letters << 'e'(4)assert letters[ 4] == 'e'assert letters[-1] == 'e'assert letters[1, 3] == ['b', 'd'](5)assert letters[2..4] == ['C', 'd', 'e'](6)
1Access the first element of the list (zero-based counting)
2Access the last element of the list with a negative index: -1 is the first element from the end of the list
3Use an assignment to set a new value for the third element of the list
4Use the<< leftShift operator to append an element at the end of the list
5Access two elements at once, returning a new list containing those two elements
6Use a range to access a range of values from the list, from a start to an end element position

As lists can be heterogeneous in nature, lists can also contain other lists to create multidimensional lists:

def multi = [[0, 1], [2, 3]](1)assert multi[1][0] == 2(2)
1Define a list of numbers
2Access the second element of the top-most list, and the first element of the inner list

1.1.8. Arrays

Groovy reuses the list notation for arrays, but to make such literals arrays,you need to explicitly define the type of the array through coercion or type declaration.

String[] arrStr = ['Ananas', 'Banana', 'Kiwi'](1)assert arrStr instanceof String[](2)assert !(arrStr instanceof List)def numArr = [1, 2, 3] as int[](3)assert numArr instanceof int[](4)assert numArr.size() == 3
1Define an array of strings using explicit variable type declaration
2Assert that we created an array of strings
3Create an array of ints with theas operator
4Assert that we created an array of primitive ints

You can also create multi-dimensional arrays:

def matrix3 = new Integer[3][3](1)assert matrix3.size() == 3Integer[][] matrix2(2)matrix2 = [[1, 2], [3, 4]]assert matrix2 instanceof Integer[][]
1You can define the bounds of a new array
2Or declare an array without specifying its bounds

Access to elements of an array follows the same notation as for lists:

String[] names = ['Cédric', 'Guillaume', 'Jochen', 'Paul']assert names[0] == 'Cédric'(1)names[2] = 'Blackdrag'(2)assert names[2] == 'Blackdrag'
1Retrieve the first element of the array
2Set the value of the third element of the array to a new value
Java-style array initialization

Groovy has always supported literal list/array definitions using square bracketsand has avoided Java-style curly braces so as not to conflict with closure definitions.In the case where the curly braces come immediately after an array type declaration however,there is no ambiguity with closure definitions,so Groovy 3 and above support that variant of the Java array initialization expression.

Examples:

def primes = new int[] {2, 3, 5, 7, 11}assert primes.size() == 5 && primes.sum() == 28assert 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 supportedString[] groovyBooks = [ 'Groovy in Action', 'Making Java Groovy' ]assert groovyBooks.every{ it.contains('Groovy') }

1.1.9. Maps

Sometimes called dictionaries or associative arrays in other languages, Groovy features maps.Maps associate keys to values, separating keys and values with colons, and each key/value pairs with commas,and the whole keys and values surrounded by square brackets.

def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF'](1)assert colors['red'] == '#FF0000'(2)assert colors.green  == '#00FF00'(3)colors['pink'] = '#FF00FF'(4)colors.yellow  = '#FFFF00'(5)assert colors.pink == '#FF00FF'assert colors['yellow'] == '#FFFF00'assert colors instanceof java.util.LinkedHashMap
1We define a map of string color names, associated with their hexadecimal-coded html colors
2We use the subscript notation to check the content associated with thered key
3We can also use the property notation to assert the color green’s hexadecimal representation
4Similarly, we can use the subscript notation to add a new key/value pair
5Or the property notation, to add theyellow color
When using names for the keys, we actually define string keys in the map.
Groovy creates maps that are actually instances ofjava.util.LinkedHashMap.

If you try to access a key which is not present in the map:

assert colors.unknown == nulldef emptyMap = [:]assert emptyMap.anyKey == null

You will retrieve anull result.

In the examples above, we used string keys, but you can also use values of other types as keys:

def numbers = [1: 'one', 2: 'two']assert numbers[1] == 'one'

Here, we used numbers as keys, as numbers can unambiguously be recognized as numbers,so Groovy will not create a string key like in our previous examples.But consider the case you want to pass a variable in lieu of the key, to have the value of that variable become the key:

def key = 'name'def person = [key: 'Guillaume'](1)assert !person.containsKey('name')(2)assert person.containsKey('key')(3)
1Thekey associated with the'Guillaume' name will actually be the"key" string, not the value associated with thekey variable
2The map doesn’t contain the'name' key
3Instead, the map contains a'key' key
You can also pass quoted strings as well as keys: ["name": "Guillaume"].This is mandatory if your key string isn’t a valid identifier,for example if you wanted to create a string key containing a dash like in: ["street-name": "Main street"].

When you need to pass variable values as keys in your map definitions, you must surround the variable or expression with parentheses:

person = [(key): 'Guillaume'](1)assert person.containsKey('name')(2)assert !person.containsKey('key')(3)
1This time, we surround thekey variable with parentheses, to instruct the parser we are passing a variable rather than defining a string key
2The map does contain thename key
3But the map doesn’t contain thekey key as before

1.2. Operators

This chapter covers the operators of the Groovy programming language.

1.2.1. Arithmetic operators

Groovy supports the usual familiar arithmetic operators you find in mathematics and in other programming languages like Java.All the Java arithmetic operators are supported. Let’s go through them in the following examples.

Normal arithmetic operators

The following binary arithmetic operators are available in Groovy:

OperatorPurposeRemarks

+

addition

-

subtraction

*

multiplication

/

division

Useintdiv() for integer division, and see the section aboutinteger division for more information on the return type of the division.

%

remainder

**

power

See the section aboutthe power operation for more information on the return type of the operation.

Here are a few examples of usage of those operators:

assert  1  + 2 == 3assert  4  - 3 == 1assert  3  * 5 == 15assert  3  / 2 == 1.5assert 10  % 3 == 1assert  2 ** 3 == 8
Unary operators

The+ and- operators are also available as unary operators:

assert +3 == 3assert -4 == 0 - 4assert -(-1) == 1(1)
1Note the usage of parentheses to surround an expression to apply the unary minus to that surrounded expression.

In terms of unary arithmetics operators, the++ (increment) and-- (decrement) operators are available,both in prefix and postfix notation:

def a = 2def b = a++ * 3(1)assert a == 3 && b == 6def c = 3def d = c-- * 2(2)assert c == 2 && d == 6def e = 1def f = ++e + 3(3)assert e == 2 && f == 5def g = 4def h = --g + 1(4)assert g == 3 && h == 4
1The postfix increment will incrementa after the expression has been evaluated and assigned intob
2The postfix decrement will decrementc after the expression has been evaluated and assigned intod
3The prefix increment will incremente before the expression is evaluated and assigned intof
4The prefix decrement will decrementg before the expression is evaluated and assigned intoh

For the unary not operator on Booleans, seeConditional operators.

Assignment arithmetic operators

The binary arithmetic operators we have seen above are also available in an assignment form:

  • +=

  • -=

  • *=

  • /=

  • %=

  • **=

Let’s see them in action:

def a = 4a += 3assert a == 7def b = 5b -= 3assert b == 2def c = 5c *= 3assert c == 15def d = 10d /= 2assert d == 5def e = 10e %= 3assert e == 1def f = 3f **= 2assert f == 9

1.2.2. Relational operators

Relational operators allow comparisons between objects, to know if two objects are the same or different,or if one is greater than, less than, or equal to the other.

The following operators are available:

OperatorPurpose

==

equal

!=

different

<

less than

<=

less than or equal

>

greater than

>=

greater than or equal

===

identical (Since Groovy 3.0.0)

!==

not identical (Since Groovy 3.0.0)

Here are some examples of simple number comparisons using these operators:

assert 1 + 2 == 3assert 3 != 4assert -2 < 3assert 2 <= 2assert 3 <= 4assert 5 > 1assert 5 >= -2

Both=== and!== are supported which are the same as calling theis() method,and negating a call to theis() method respectively.

import groovy.transform.EqualsAndHashCode@EqualsAndHashCodeclass Creature { String type }def cat = new Creature(type: 'cat')def copyCat = catdef lion = new Creature(type: 'cat')assert cat.equals(lion) // Java logical equalityassert cat == lion      // Groovy shorthand operatorassert cat.is(copyCat)  // Groovy identityassert cat === copyCat  // operator shorthandassert cat !== lion     // negated operator shorthand

1.2.3. Logical operators

Groovy offers three logical operators for boolean expressions:

  • &&: logical "and"

  • ||: logical "or"

  • !: logical "not"

Let’s illustrate them with the following examples:

assert !false(1)assert true && true(2)assert true || false(3)
1"not" false is true
2true "and" true is true
3true "or" false is true
Precedence

The logical "not" has a higher priority than the logical "and".

assert (!false && false) == false(1)
1Here, the assertion is true (as the expression in parentheses is false), because "not" has a higher precedence than "and", so it only applies to the first "false" term; otherwise, it would have applied to the result of the "and", turned it into true, and the assertion would have failed

The logical "and" has a higher priority than the logical "or".

assert true || true && false(1)
1Here, the assertion is true, because "and" has a higher precedence than "or", therefore the "or" is executed last and returns true, having one true argument; otherwise, the "and" would have executed last and returned false, having one false argument, and the assertion would have failed
Short-circuiting

The logical|| operator supports short-circuiting: if the left operand is true, it knows that the result will be true in any case, so it won’t evaluate the right operand.The right operand will be evaluated only if the left operand is false.

Likewise for the logical&& operator: if the left operand is false, it knows that the result will be false in any case, so it won’t evaluate the right operand.The right operand will be evaluated only if the left operand is true.

boolean checkIfCalled() {(1)    called = true}called = falsetrue || checkIfCalled()assert !called(2)called = falsefalse || checkIfCalled()assert called(3)called = falsefalse && checkIfCalled()assert !called(4)called = falsetrue && checkIfCalled()assert called(5)
1We create a function that sets thecalled flag to true whenever it’s called
2In the first case, after resetting the called flag, we confirm that if the left operand to|| is true, the function is not called, as|| short-circuits the evaluation of the right operand
3In the second case, the left operand is false and so the function is called, as indicated by the fact our flag is now true
4Likewise for&&, we confirm that the function is not called with a false left operand
5But the function is called with a true left operand

1.2.4. Bitwise and bit shift operators

Bitwise operators

Groovy offers four bitwise operators:

  • &: bitwise "and"

  • |: bitwise "or"

  • ^: bitwise "xor" (exclusive "or")

  • ~: bitwise negation

Bitwise operators can be applied on arguments which are of typebyte,short,int,long, orBigInteger.If one of the arguments is aBigInteger, the result will be of typeBigInteger;otherwise, if one of the arguments is along, the result will be of typelong;otherwise, the result will be of typeint:

int a = 0b00101010assert a == 42int b = 0b00001000assert b == 8assert (a & a) == a(1)assert (a & b) == b(2)assert (a | a) == a(3)assert (a | b) == a(4)int mask = 0b11111111(5)assert ((a ^ a) & mask) == 0b00000000(6)assert ((a ^ b) & mask) == 0b00100010(7)assert ((~a) & mask)    == 0b11010101(8)
1bitwise and
2bitwise and returns common bits
3bitwise or
4bitwise or returns all '1' bits
5setting a mask to check only the last 8 bits
6bitwise exclusive or on self returns 0
7bitwise exclusive or
8bitwise negation

It’s worth noting that the internal representation of primitive types follow theJava Language Specification. In particular,primitive types are signed, meaning that for a bitwise negation, it is always good to use a mask to retrieve only the necessary bits.

In Groovy, bitwise operators areoverloadable, meaning that you can define the behavior of those operators for any kind of object.

Bit shift operators

Groovy offers three bit shift operators:

  • <<: left shift

  • >>: right shift

  • >>>: right shift unsigned

All three operators are applicable where the left argument is of typebyte,short,int, orlong.The first two operators can also be applied where the left argument is of typeBigInteger.If the left argument is aBigInteger, the result will be of typeBigInteger;otherwise, if the left argument is along, the result will be of typelong;otherwise, the result will be of typeint:

assert 12.equals(3 << 2)(1)assert 24L.equals(3L << 3)(1)assert 48G.equals(3G << 4)(1)assert 4095 == -200 >>> 20assert -1 == -200 >> 20assert 2G == 5G >> 1assert -3G == -5G >> 1
1equals method used instead of== to confirm result type

In Groovy, bit shift operators areoverloadable, meaning that you can define the behavior of those operators for any kind of object.

1.2.5. Conditional operators

Not operator

The "not" operator is represented with an exclamation mark (!) and inverts the result of the underlying boolean expression. Inparticular, it is possible to combine thenot operator with theGroovy truth:

assert (!true)    == false(1)assert (!'foo')   == false(2)assert (!'')      == true(3)
1the negation oftrue isfalse
2'foo' is a non-empty string, evaluating totrue, so negation returnsfalse
3'' is an empty string, evaluating tofalse, so negation returnstrue
Ternary operator

The ternary operator is a shortcut expression that is equivalent to an if/else branch assigning some value to a variable.

Instead of:

if (string!=null && string.length()>0) {    result = 'Found'} else {    result = 'Not found'}

You can write:

result = (string!=null && string.length()>0) ? 'Found' : 'Not found'

The ternary operator is also compatible with theGroovy truth, so you can make it even simpler:

result = string ? 'Found' : 'Not found'
Elvis operator

The "Elvis operator" is a shortening of the ternary operator. One instance of where this is handy is for returninga 'sensible default' value if an expression resolves tofalse-ish (as inGroovy truth). A simple example might look like this:

displayName = user.name ? user.name : 'Anonymous'(1)displayName = user.name ?: 'Anonymous'(2)
1with the ternary operator, you have to repeat the value you want to assign
2with the Elvis operator, the value, which is tested, is used if it is notfalse-ish

Usage of the Elvis operator reduces the verbosity of your code and reduces the risks of errors in case of refactorings,by removing the need to duplicate the expression which is tested in both the condition and the positive return value.

Elvis assignment operator

Groovy 3.0.0 introduces the Elvis operator, for example:

import groovy.transform.ToString@ToString(includePackage = false)class Element {    String name    int atomicNumber}def he = new Element(name: 'Helium')he.with {    name = name ?: 'Hydrogen'   // existing Elvis operator    atomicNumber ?= 2           // new Elvis assignment shorthand}assert he.toString() == 'Element(Helium, 2)'

1.2.6. Object operators

Safe navigation operator

The Safe Navigation operator is used to avoid aNullPointerException. Typically when you have a reference to an objectyou might need to verify that it is notnull before accessing methods or properties of the object. To avoid this, the safenavigation operator will simply returnnull instead of throwing an exception, like so:

def person = Person.find { it.id == 123 }(1)def name = person?.name(2)assert name == null(3)
1find will return anull instance
2use of the null-safe operator prevents from aNullPointerException
3result isnull
Direct field access operator

Normally in Groovy, when you write code like this:

class User {    public final String name(1)    User(String name) { this.name = name}    String getName() { "Name: $name" }(2)}def user = new User('Bob')assert user.name == 'Name: Bob'(3)
1public fieldname
2a getter forname that returns a custom string
3calls the getter

Theuser.name call triggers a call to the property of the same name, that is to say, here, to the getter forname. Ifyou want to retrieve the field instead of calling the getter, you can use the direct field access operator:

assert user.@name == 'Bob'(1)
1use of.@ forces usage of the field instead of the getter
Method pointer operator

The method pointer operator (.&) can be used to store a reference to a method in a variable,in order to call it later:

def str = 'example of method reference'(1)def fun = str.&toUpperCase(2)def upper = fun()(3)assert upper == str.toUpperCase()(4)
1thestr variable contains aString
2we store a reference to thetoUpperCase method on thestr instance inside a variable namedfun
3fun can be called like a regular method
4we can check that the result is the same as if we had called it directly onstr

There are multiple advantages in using method pointers. First of all, the type of such a method pointer isagroovy.lang.Closure, so it can be used in any place a closure would be used. In particular, it is suitable toconvert an existing method for the needs of the strategy pattern:

def transform(List elements, Closure action) {(1)    def result = []    elements.each {        result << action(it)    }    result}String describe(Person p) {(2)    "$p.name is $p.age"}def action = this.&describe(3)def list = [    new Person(name: 'Bob',   age: 42),    new Person(name: 'Julia', age: 35)](4)assert transform(list, action) == ['Bob is 42', 'Julia is 35'](5)
1thetransform method takes each element of the list and calls theaction closure on them, returning a new list
2we define a function that takes aPerson and returns aString
3we create a method pointer on that function
4we create the list of elements we want to collect the descriptors
5the method pointer can be used where aClosure was expected

Method pointers are bound by the receiver and a method name. Arguments are resolved at runtime, meaning that if you havemultiple methods with the same name, the syntax is not different, only resolution of the appropriate method to be calledwill be done at runtime:

def doSomething(String str) { str.toUpperCase() }(1)def doSomething(Integer x) { 2*x }(2)def reference = this.&doSomething(3)assert reference('foo') == 'FOO'(4)assert reference(123)   == 246(5)
1define an overloadeddoSomething method accepting aString as an argument
2define an overloadeddoSomething method accepting anInteger as an argument
3create a single method pointer ondoSomething, without specifying argument types
4using the method pointer with aString calls theString version ofdoSomething
5using the method pointer with anInteger calls theInteger version ofdoSomething

To align with Java 8 method reference expectations, in Groovy 3 and above, you can usenew as themethod name to obtain a method pointer to the constructor:

def foo  = BigInteger.&newdef fortyTwo = foo('42')assert fortyTwo == 42G

Also in Groovy 3 and above, you can obtain a method pointer to an instance method of a class.This method pointer takes an additional parameter being the receiver instance toinvoke the method on:

def instanceMethod = String.&toUpperCaseassert instanceMethod('foo') == 'FOO'

For backwards compatibility, any static methods that happen to have the correctparameters for the call will be given precedence over instance methods for this case.

Method reference operator

The Parrot parser in Groovy 3+ supports the Java 8+ method reference operator.The method reference operator (::) can be used to reference a method or constructorin contexts expecting a functional interface. This overlaps somewhat with the functionalityprovided by Groovy’s method pointer operator. Indeed, for dynamic Groovy, the methodreference operator is just an alias for the method pointer operator.For static Groovy, the operator results in bytecode similar to the bytecodethat Java would produce for the same context.

Some examples highlighting various supported method reference cases are shown in the following script:

import groovy.transform.CompileStaticimport static java.util.stream.Collectors.toList@CompileStaticvoid methodRefs() {    assert 6G == [1G, 2G, 3G].stream().reduce(0G, BigInteger::add)(1)    assert [4G, 5G, 6G] == [1G, 2G, 3G].stream().map(3G::add).collect(toList())(2)    assert [1G, 2G, 3G] == [1L, 2L, 3L].stream().map(BigInteger::valueOf).collect(toList())(3)    assert [1G, 2G, 3G] == [1L, 2L, 3L].stream().map(3G::valueOf).collect(toList())(4)}methodRefs()
1class instance method reference: add(BigInteger val) is an instance method in BigInteger
2object instance method reference: add(BigInteger val) is an instance method for object 3G
3class static method reference: valueOf(long val) is a static method for class BigInteger
4object static method reference: valueOf(long val) is a static method for object 3G (some consider this bad style in normal circumstances)

Some examples highlighting various supported constructor reference cases are shown in the following script:

@CompileStaticvoid constructorRefs() {    assert [1, 2, 3] == ['1', '2', '3'].stream().map(Integer::valueOf).collect(toList())(1)    def result = [1, 2, 3].stream().toArray(Integer[]::new)(2)    assert result instanceof Integer[]    assert result.toString() == '[1, 2, 3]'}constructorRefs()
1class constructor reference
2array constructor reference

1.2.7. Regular expression operators

Pattern operator

The pattern operator (~) provides a simple way to create ajava.util.regex.Pattern instance:

def p = ~/foo/assert p instanceof Pattern

while in general, you find the pattern operator with an expression in a slashy-string, it can be used with any kind ofString in Groovy:

p = ~'foo'(1)p = ~"foo"(2)p = ~$/dollar/slashy $ string/$(3)p = ~"${pattern}"(4)
1using single quote strings
2using double quotes strings
3the dollar-slashy string lets you use slashes and the dollar sign without having to escape them
4you can also use a GString!
While you can use most String forms with the Pattern, Find and Match operators,we recommend using the slashy string most of the time to save having toremember the otherwise needed escaping requirements.
Find operator

Alternatively to building a pattern, you can use the find operator=~ to directly create ajava.util.regex.Matcher instance:

def text = "some text to match"def m = text =~ /match/(1)assert m instanceof Matcher(2)if (!m) {(3)    throw new RuntimeException("Oops, text not found!")}
1=~ creates a matcher against thetext variable, using the pattern on the right hand side
2the return type of=~ is aMatcher
3equivalent to callingif (!m.find(0))

Since aMatcher coerces to aboolean by calling itsfind method, the=~ operator is consistent with the simpleuse of Perl’s=~ operator, when it appears as a predicate (inif,?:, etc.). When the intent is to iterate overmatches of the specified pattern (inwhile, etc.) callfind() directly on the matcher or use theiterator DGM.

Match operator

The match operator (==~) is a slight variation of the find operator, that does not return aMatcher but a booleanand requires a strict match of the input string:

m = text ==~ /match/(1)assert m instanceof Boolean(2)if (m) {(3)    throw new RuntimeException("Should not reach that point!")}
1==~ matches the subject with the regular expression, but match must be strict
2the return type of==~ is therefore aboolean
3equivalent to callingif (text ==~ /match/)
Comparing Find vs Match operators

Typically, the match operator is used when the pattern involves a single exact match, otherwisethe find operator might be more useful.

assert 'two words' ==~ /\S+\s+\S+/assert 'two words' ==~ /^\S+\s+\S+$/(1)assert !(' leading space' ==~ /\S+\s+\S+/)(2)def m1 = 'two words' =~ /^\S+\s+\S+$/assert m1.size() == 1(3)def m2 = 'now three words' =~ /^\S+\s+\S+$/(4)assert m2.size() == 0(5)def m3 = 'now three words' =~ /\S+\s+\S+/assert m3.size() == 1(6)assert m3[0] == 'now three'def m4 = ' leading space' =~ /\S+\s+\S+/assert m4.size() == 1(7)assert m4[0] == 'leading space'def m5 = 'and with four words' =~ /\S+\s+\S+/assert m5.size() == 2(8)assert m5[0] == 'and with'assert m5[1] == 'four words'
1equivalent, but explicit ^ and $ are discouraged since they aren’t needed
2no match because of leading space
3one match
4^ and $ indicate exact match required
5zero matches
6one match, greedily starting at first word
7one match, ignores leading space
8two matches

1.2.8. Other operators

Spread operator

The Spread-dot Operator (*.), often abbreviated to just Spread Operator, is used to invoke an action on all itemsof an aggregate object. It is equivalent to calling the action on each item and collecting the result into a list:

class Car {    String make    String model}def cars = [       new Car(make: 'Peugeot', model: '508'),       new Car(make: 'Renault', model: 'Clio')](1)def makes = cars*.make(2)assert makes == ['Peugeot', 'Renault'](3)
1build a list ofCar items. The list is an aggregate of objects.
2call the spread operator on the list, accessing themake property of each item
3returns a list of strings corresponding to the collection ofmake items

The expressioncars*.make is equivalent tocars.collect{ it.make }.Groovy’s GPath notation allows a short-cut when the referenced propertyisn’t a property of the containing list, in that case it is automaticallyspread. In the previously mentioned case, the expressioncars.make canbe used, though retaining the explicit spread-dot operator is often recommended.

The spread operator is null-safe, meaning that if an element of the collection is null,it will return null instead of throwing aNullPointerException:

cars = [   new Car(make: 'Peugeot', model: '508'),   null,(1)   new Car(make: 'Renault', model: 'Clio')]assert cars*.make == ['Peugeot', null, 'Renault'](2)assert null*.make == null(3)
1build a list for which one of the elements isnull
2using the spread operator willnot throw aNullPointerException
3the receiver might also be null, in which case the return value isnull

The spread operator can be used on any class which implements theIterable interface:

class Component {    Integer id    String name}class CompositeObject implements Iterable<Component> {    def components = [        new Component(id: 1, name: 'Foo'),        new Component(id: 2, name: 'Bar')]    @Override    Iterator<Component> iterator() {        components.iterator()    }}def composite = new CompositeObject()assert composite*.id == [1,2]assert composite*.name == ['Foo','Bar']

Use multiple invocations of the spread-dot operator (herecars*.models*.name) whenworking with aggregates of data structures which themselves contain aggregates:

class Make {    String name    List<Model> models}@Canonicalclass Model {    String name}def cars = [    new Make(name: 'Peugeot',             models: [new Model('408'), new Model('508')]),    new Make(name: 'Renault',             models: [new Model('Clio'), new Model('Captur')])]def makes = cars*.nameassert makes == ['Peugeot', 'Renault']def models = cars*.models*.nameassert models == [['408', '508'], ['Clio', 'Captur']]assert models.sum() == ['408', '508', 'Clio', 'Captur'] // flatten one levelassert models.flatten() == ['408', '508', 'Clio', 'Captur'] // flatten all levels (one in this case)

Consider using thecollectNested DGM method instead of the spread-dot operator for collections of collections:

class Car {    String make    String model}def cars = [   [       new Car(make: 'Peugeot', model: '408'),       new Car(make: 'Peugeot', model: '508')   ], [       new Car(make: 'Renault', model: 'Clio'),       new Car(make: 'Renault', model: 'Captur')   ]]def models = cars.collectNested{ it.model }assert models == [['408', '508'], ['Clio', 'Captur']]
Spreading method arguments

There may be situations when the arguments of a method call can be found in a list that you need to adapt to the methodarguments. In such situations, you can use the spread operator to call the method. For example, imagine you have thefollowing method signature:

int function(int x, int y, int z) {    x*y+z}

then if you have the following list:

def args = [4,5,6]

you can call the method without having to define intermediate variables:

assert function(*args) == 26

It is even possible to mix normal arguments with spread ones:

args = [4]assert function(*args,5,6) == 26
Spread list elements

When used inside a list literal, the spread operator acts as if the spread element contents were inlined into the list:

def items = [4,5](1)def list = [1,2,3,*items,6](2)assert list == [1,2,3,4,5,6](3)
1items is a list
2we want to insert the contents of theitems list directly intolist without having to calladdAll
3the contents ofitems has been inlined intolist
Spread map elements

The spread map operator works in a similar manner as the spread list operator, but for maps. It allows you to inlinethe contents of a map into another map literal, like in the following example:

def m1 = [c:3, d:4](1)def map = [a:1, b:2, *:m1](2)assert map == [a:1, b:2, c:3, d:4](3)
1m1 is the map that we want to inline
2we use the*:m1 notation to spread the contents ofm1 intomap
3map contains all the elements ofm1

The position of the spread map operator is relevant, like illustrated in the following example:

def m1 = [c:3, d:4](1)def map = [a:1, b:2, *:m1, d: 8](2)assert map == [a:1, b:2, c:3, d:8](3)
1m1 is the map that we want to inline
2we use the*:m1 notation to spread the contents ofm1 intomap, but redefine the keydafter spreading
3map contains all the expected keys, butd was redefined
Range operator

Groovy supports the concept of ranges and provides a notation (..) to create ranges of objects:

def range = 0..5(1)assert (0..5).collect() == [0, 1, 2, 3, 4, 5](2)assert (0..<5).collect() == [0, 1, 2, 3, 4](3)assert (0<..5).collect() == [1, 2, 3, 4, 5](4)assert (0<..<5).collect() == [1, 2, 3, 4](5)assert (0..5) instanceof List(6)assert (0..5).size() == 6(7)
1a simple range of integers, stored into a local variable
2anIntRange, with inclusive bounds
3anIntRange, with exclusive upper bound
4anIntRange, with exclusive lower bound
5anIntRange, with exclusive lower and upper bounds
6agroovy.lang.Range implements theList interface
7meaning that you can call thesize method on it

Ranges implementation is lightweight, meaning that only the lower and upper bounds are stored. You can create a rangefrom anyComparable object that hasnext() andprevious() methods to determine the next / previous item in the range.For example, you can create a range of characters this way:

assert ('a'..'d').collect() == ['a','b','c','d']
Spaceship operator

The spaceship operator (<=>) delegates to thecompareTo method:

assert (1 <=> 1) == 0assert (1 <=> 2) == -1assert (2 <=> 1) == 1assert ('a' <=> 'z') == -1
Subscript operator

The subscript operator is a shorthand notation forgetAt orputAt, depending on whether you find it onthe left hand side or the right hand side of an assignment:

def list = [0,1,2,3,4]assert list[2] == 2(1)list[2] = 4(2)assert list[0..2] == [0,1,4](3)list[0..2] = [6,6,6](4)assert list == [6,6,6,3,4](5)
1[2] can be used instead ofgetAt(2)
2if on left hand side of an assignment, will callputAt
3getAt also supports ranges
4so doesputAt
5the list is mutated

The subscript operator, in combination with a custom implementation ofgetAt/putAt is a convenient way for destructuringobjects:

class User {    Long id    String name    def getAt(int i) {(1)        switch (i) {            case 0: return id            case 1: return name        }        throw new IllegalArgumentException("No such element $i")    }    void putAt(int i, def value) {(2)        switch (i) {            case 0: id = value; return            case 1: name = value; return        }        throw new IllegalArgumentException("No such element $i")    }}def user = new User(id: 1, name: 'Alex')(3)assert user[0] == 1(4)assert user[1] == 'Alex'(5)user[1] = 'Bob'(6)assert user.name == 'Bob'(7)
1theUser class defines a customgetAt implementation
2theUser class defines a customputAt implementation
3create a sample user
4using the subscript operator with index 0 allows retrieving the user id
5using the subscript operator with index 1 allows retrieving the user name
6we can use the subscript operator to write to a property thanks to the delegation toputAt
7and check that it’s really the propertyname which was changed
Safe index operator

Groovy 3.0.0 introduces safe indexing operator, i.e.?[], which is similar to?.. For example:

String[] array = ['a', 'b']assert 'b' == array?[1]      // get using normal array indexarray?[1] = 'c'              // set using normal array indexassert 'c' == array?[1]array = nullassert null == array?[1]     // return null for all index valuesarray?[1] = 'c'              // quietly ignore attempt to set valueassert null == array?[1]def personInfo = [name: 'Daniel.Sun', location: 'Shanghai']assert 'Daniel.Sun' == personInfo?['name']      // get using normal map indexpersonInfo?['name'] = 'sunlan'                  // set using normal map indexassert 'sunlan' == personInfo?['name']personInfo = nullassert null == personInfo?['name']              // return null for all map valuespersonInfo?['name'] = 'sunlan'                  // quietly ignore attempt to set valueassert null == personInfo?['name']
Membership operator

The membership operator (in) is equivalent to calling theisCase method. In the context of aList, it is equivalentto callingcontains, like in the following example:

def list = ['Grace','Rob','Emmy']assert ('Emmy' in list)(1)assert ('Alex' !in list)(2)
1equivalent to callinglist.contains('Emmy') orlist.isCase('Emmy')
2membership negation equivalent to calling!list.contains('Emmy') or!list.isCase('Emmy')
Identity operator

In Groovy, using== to test equality is different from using the same operator in Java. In Groovy, it is callingequals.If you want to compare reference equality, you should useis like in the following example:

def list1 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3'](1)def list2 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3'](2)assert list1 == list2(3)assert !list1.is(list2)(4)assert list1 !== list2(5)
1Create a list of strings
2Create another list of strings containing the same elements
3using==, we test object equality, equivalent tolist1.equals(list2) in Java
4usingis, we can check that references are distinct, equivalent tolist1 == list2 in Java
5using=== or!== (supported and recommended since Groovy 3.0.0), we can also check whether references are distinct or not, equivalent tolist1 == list2 andlist1 != list2 in Java
Coercion operator

The coercion operator (as) is a variant of casting. Coercion converts object from one type to anotherwithout thembeing compatible for assignment. Let’s take an example:

String input = '42'Integer num = (Integer) input(1)
1String is not assignable to anInteger, so it will produce aClassCastException at runtime

This can be fixed by usingcoercion instead:

String input = '42'Integer num = input as Integer(1)
1String is not assignable to anInteger, but use ofas willcoerce it to anInteger

When an object is coerced into another, unless the target type is the same as the source type, coercion will return anew object. The rules of coercion differ depending on the source and target types, and coercion may fail if no conversionrules are found. Custom conversion rules may be implemented thanks to theasType method:

class Identifiable {    String name}class User {    Long id    String name    def asType(Class target) {(1)        if (target == Identifiable) {            return new Identifiable(name: name)        }        throw new ClassCastException("User cannot be coerced into $target")    }}def u = new User(name: 'Xavier')(2)def p = u as Identifiable(3)assert p instanceof Identifiable(4)assert !(p instanceof User)(5)
1theUser class defines a custom conversion rule fromUser toIdentifiable
2we create an instance ofUser
3we coerce theUser instance into anIdentifiable
4the target is an instance ofIdentifiable
5the target is not an instance ofUser anymore
Diamond operator

The diamond operator (<>) is a syntactic sugar only operator added to support compatibility with the operator of thesame name in Java 7. It is used to indicate that generic types should be inferred from the declaration:

List<String> strings = new LinkedList<>()

In dynamic Groovy, this is totally unused. In statically type checked Groovy, it is also optional since the Groovytype checker performs type inference whether this operator is present or not.

Call operator

The call operator() is used to call a method namedcall implicitly. For any object which defines acall method, you can omit the.call part and use the call operator instead:

class MyCallable {    int call(int x) {(1)        2*x    }}def mc = new MyCallable()assert mc.call(2) == 4(2)assert mc(2) == 4(3)
1MyCallable defines a method namedcall. Note that it doesn’t need to implementjava.util.concurrent.Callable
2we can call the method using the classic method call syntax
3or we can omit.call thanks to the call operator

1.2.9. Operator precedence

The table below lists all groovy operators in order of precedence.

LevelOperator(s)Name(s)

1

new  ()

object creation, explicit parentheses

()  {}  []

method call, closure, literal list/map

.  .&  .@

member access, method closure, field/attribute access

?.  *  *.  *:

safe dereferencing, spread, spread-dot, spread-map

~  !  (type)

bitwise negate/pattern, not, typecast

[]  ?[]  ++  --

list/map/array (safe) index, post inc/decrement

2

**

power

3

+` {nbsp} `--` {nbsp} `  -

pre inc/decrement, unary plus, unary minus

4

*  /  %

multiply, div, remainder

5

+  -

addition, subtraction

6

<<  >>  >>>  ..  ..<  <..<  <..

left/right (unsigned) shift, inclusive/exclusive ranges

7

<  <=  >  >=  in  !in  instanceof  !instanceof  as

less/greater than/or equal, in, not in, instanceof, not instanceof, type coercion

8

==  !=  <=>  ===  !==

equals, not equals, compare to, identical to, not identical to

=~  ==~

regex find, regex match

9

&

binary/bitwise and

10

^

binary/bitwise xor

11

|

binary/bitwise or

12

&&

logical and

13

||

logical or

14

? :

ternary conditional

?:

elvis operator

15

=  **=  *=  /=  %=  +=  -=  
<<=  >>=  >>>=  &=  ^=  |=    ?=

various assignments

1.2.10. Operator overloading

Groovy allows you to overload the various operators so that they can be used with your own classes. Consider this simpleclass:

class Bucket {    int size    Bucket(int size) { this.size = size }    Bucket plus(Bucket other) {(1)        return new Bucket(this.size + other.size)    }}
1Bucket implements a special method calledplus()

Just by implementing theplus() method, theBucket class can now be used with the+ operator like so:

def b1 = new Bucket(4)def b2 = new Bucket(11)assert (b1 + b2).size == 15(1)
1The twoBucket objects can be added together with the+ operator

All (non-comparator) Groovy operators have a corresponding method that you can implement in your own classes. The onlyrequirements are that your method is public, has the correct name, and has the correct number of arguments. The argumenttypes depend on what types you want to support on the right hand side of the operator. For example, you could supportthe statement

assert (b1 + 11).size == 15

by implementing theplus() method with this signature:

Bucket plus(int capacity) {    return new Bucket(this.size + capacity)}

Here is a complete list of the operators and their corresponding methods:

OperatorMethodOperatorMethod

+

a.plus(b)

a[b]

a.getAt(b)

-

a.minus(b)

a[b] = c

a.putAt(b, c)

*

a.multiply(b)

a in b

b.isCase(a)

/

a.div(b)

<<

a.leftShift(b)

%

a.mod(b)

>>

a.rightShift(b)

**

a.power(b)

>>>

a.rightShiftUnsigned(b)

|

a.or(b)

++

a.next()

&

a.and(b)

--

a.previous()

^

a.xor(b)

+a

a.positive()

as

a.asType(b)

-a

a.negative()

a()

a.call()

~a

a.bitwiseNegate()

1.3. Program structure

This chapter covers the program structure of the Groovy programming language.

1.3.1. Package names

Package names play exactly the same role as in Java. They allow us to separate the code base without any conflicts. Groovy classes must specify their package before the class definition, else the default package is assumed.

Defining a package is very similar to Java:

// defining a package named com.yoursitepackage com.yoursite

To refer to some classFoo in thecom.yoursite.com package you will need to use the fully qualified namecom.yoursite.com.Foo, or else you can use animport statement as we’ll see below.

1.3.2. Imports

In order to refer to any class you need a qualified reference to its package. Groovy follows Java’s notion of allowingimport statement to resolve class references.

For example, Groovy provides several builder classes, such asMarkupBuilder.MarkupBuilder is inside the packagegroovy.xml so in order to use this class, you need toimport it as shown:

// importing the class MarkupBuilderimport groovy.xml.MarkupBuilder// using the imported class to create an objectdef xml = new MarkupBuilder()assert xml != null
Default imports

Default imports are the imports that Groovy language provides by default. For example look at the following code:

new Date()

The same code in Java needs an import statement toDate class like this: import java.util.Date. Groovy by default imports these classes for you.

The below imports are added by groovy for you:

import java.lang.*import java.util.*import java.io.*import java.net.*import groovy.lang.*import groovy.util.*import java.math.BigIntegerimport java.math.BigDecimal

This is done because the classes from these packages are most commonly used. By importing these boilerplate code is reduced.

Simple import

A simple import is an import statement where you fully define the class name along with the package. For example the import statement import groovy.xml.MarkupBuilder in the code below is a simple import which directly refers to a class inside a package.

// importing the class MarkupBuilderimport groovy.xml.MarkupBuilder// using the imported class to create an objectdef xml = new MarkupBuilder()assert xml != null
Star import

Groovy, like Java, provides a special way to import all classes from a package using*, the so-called star import.MarkupBuilder is a class which is in packagegroovy.xml, alongside another class calledStreamingMarkupBuilder. In case you need to use both classes, you can do:

import groovy.xml.MarkupBuilderimport groovy.xml.StreamingMarkupBuilderdef markupBuilder = new MarkupBuilder()assert markupBuilder != nullassert new StreamingMarkupBuilder() != null

That’s perfectly valid code. But with a* import, we can achieve the same effect with just one line. The star imports all the classes under packagegroovy.xml:

import groovy.xml.*def markupBuilder = new MarkupBuilder()assert markupBuilder != nullassert new StreamingMarkupBuilder() != null

One problem with* imports is that they can clutter your local namespace. But with the kinds of aliasing provided by Groovy, this can be solved easily.

Static import

Groovy’s static import capability allows you to reference imported classes as if they were static methods in your own class:

import static Boolean.FALSEassert !FALSE //use directly, without Boolean prefix!

This is similar to Java’s static import capability but is a more dynamic than Java in that it allows you to define methods with the same name as an imported method as long as you have different types:

import static java.lang.String.format(1)class SomeClass {    String format(Integer i) {(2)        i.toString()    }    static void main(String[] args) {        assert format('String') == 'String'(3)        assert new SomeClass().format(Integer.valueOf(1)) == '1'    }}
1static import of method
2declaration of method with same name as method statically imported above, but with a different parameter type
3compile error in java, but is valid groovy code

If you have the same types, the imported class takes precedence.

Static import aliasing

Static imports with theas keyword provide an elegant solution to namespace problems. Suppose you want to get aCalendar instance, using itsgetInstance() method. It’s a static method, so we can use a static import. But instead of callinggetInstance() every time, which can be misleading when separated from its class name, we can import it with an alias, to increase code readability:

import static Calendar.getInstance as nowassert now().class == Calendar.getInstance().class

Now, that’s clean!

Static star import

A static star import is very similar to the regular star import. It will import all the static methods from the given class.

For example, lets say we need to calculate sines and cosines for our application.The classjava.lang.Math has static methods namedsin andcos which fit our need. With the help of a static star import, we can do:

import static java.lang.Math.*assert sin(0) == 0.0assert cos(0) == 1.0

As you can see, we were able to access the methodssin andcos directly, without theMath. prefix.

Import aliasing

With type aliasing, we can refer to a fully qualified class name using a name of our choice. This can be done with theas keyword, as before.

For example we can importjava.sql.Date asSQLDate and use it in the same file asjava.util.Date without having to use the fully qualified name of either class:

import java.util.Dateimport java.sql.Date as SQLDateDate utilDate = new Date(1000L)SQLDate sqlDate = new SQLDate(1000L)assert utilDate instanceof java.util.Dateassert sqlDate instanceof java.sql.Date

1.3.3. Scripts versus classes

public static void main vs script

Groovy supports both scripts and classes. Take the following code for example:

Main.groovy
class Main {(1)    static void main(String... args) {(2)        println 'Groovy world!'(3)    }}
1define aMain class, the name is arbitrary
2thepublic static void main(String[]) method is usable as the main method of the class
3the main body of the method

This is typical code that you would find coming from Java, where codehas to be embedded into a class to be executable.Groovy makes it easier, the following code is equivalent:

Main.groovy
println 'Groovy world!'

A script can be considered as a class without needing to declare it, with some differences.

Script class

Agroovy.lang.Script is always compiled into a class. The Groovy compiler will compile the class for you,with the body of the script copied into arun method. The previous example is therefore compiled as if it was thefollowing:

Main.groovy
import org.codehaus.groovy.runtime.InvokerHelperclass Main extends Script {(1)    def run() {(2)        println 'Groovy world!'(3)    }    static void main(String[] args) {(4)        InvokerHelper.runScript(Main, args)(5)    }}
1TheMain class extends thegroovy.lang.Script class
2groovy.lang.Script requires arun method returning a value
3the script body goes into therun method
4themain method is automatically generated
5and delegates the execution of the script on therun method

If the script is in a file, then the base name of the file is used to determine the name of the generated script class.In this example, if the name of the file isMain.groovy, then the script class is going to beMain.

Methods

It is possible to define methods into a script, as illustrated here:

int fib(int n) {    n < 2 ? 1 : fib(n-1) + fib(n-2)}assert fib(10)==89

You can also mix methods and code. The generated script class will carry all methods into the script class, andassemble all script bodies into therun method:

println 'Hello'(1)int power(int n) { 2**n }(2)println "2^6==${power(6)}"(3)
1script begins
2a method is defined within the script body
3and script continues

This code is internally converted into:

import org.codehaus.groovy.runtime.InvokerHelperclass Main extends Script {    int power(int n) { 2** n}(1)    def run() {        println 'Hello'(2)        println "2^6==${power(6)}"(3)    }    static void main(String[] args) {        InvokerHelper.runScript(Main, args)    }}
1thepower method is copied as is into the generated script class
2first statement is copied into therun method
3second statement is copied into therun method
Even if Groovy creates a class from your script, it is totally transparent for the user. In particular, scriptsare compiled to bytecode, and line numbers are preserved. This implies that if an exception is thrown in a script,the stack trace will show line numbers corresponding to the original script, not the generated code that we have shown.
Variables

Variables in a script do not require a type definition. This means that this script:

int x = 1int y = 2assert x+y == 3

will behave the same as:

x = 1y = 2assert x+y == 3

However, there is a semantic difference between the two:

  • if the variable is declared as in the first example, it is alocal variable. It will be declared in therunmethod that the compiler will generate and willnot be visible outside of the script main body. In particular, sucha variable willnot be visible in other methods of the script

  • if the variable is undeclared, it goes into thegroovy.lang.Script#getBinding(). The binding isvisible from the methods, and is especially important if you use a script to interact with an application and need toshare data between the script and the application. Readers might refer to theintegration guide for more information.

Another approach to making a variable visible to all methods, is to use the@Field annotation.A variable annotated this way will become a field of the generated script class and,as for local variables, access won’t involve the scriptBinding.While not recommended, if you have a local variable or script field with the same name as a binding variable,you can usebinding.varName to access the binding variable.

1.4. Object orientation

This chapter covers the object-oriented aspects of the Groovy programming language.

1.4.1. Types

Primitive types

Groovy supports the same primitive types as defined by theJava Language Specification:

  • integral types:byte (8 bit),short (16 bit),int (32 bit) andlong (64 bit)

  • floating-point types:float (32 bit) anddouble (64 bit)

  • theboolean type (one oftrue orfalse)

  • thechar type (16 bit, usable as a numeric type, representing a UTF-16 code)

Also like Java, Groovy uses the respective wrapper classes when objects corresponding to anyof the primitive types are required:

Table 4. primitive wrappers
Primitive typeWrapper class

boolean

Boolean

char

Character

short

Short

int

Integer

long

Long

float

Float

double

Double

Automatic boxing and unboxing occur when, for instance, calling a method requiringthe wrapper class and passing it a primitive variable as the parameter, or vice-versa.This is similar to Java but Groovy takes the idea further.

In most scenarios, you can treat a primitive just like it was the full object wrapper equivalent.For instance, you can call.toString() or.equals(other) on a primitive.Groovy autowraps and unwraps between references and primitives as needed.

Here’s an example usingint which is declared as a static field in a class (discussed shortly):

class Foo {    static int i}assert Foo.class.getDeclaredField('i').type == int.class(1)assert Foo.i.class != int.class && Foo.i.class == Integer.class(2)
1Primitive type is respected in the bytecode
2Looking at the field at runtime shows it has been autowrapped

Now you may be concerned that this means every time you use a mathematical operator on a reference to a primitivethat you’ll incur the cost of unboxing and reboxing the primitive. But this is not the case, as Groovy will compileyour operators into theirmethod equivalents and uses those instead.Additionally, Groovy will automatically unbox to a primitive when calling a Java method that takes a primitiveparameter and automatically box primitive method return values from Java. However, be aware there are somedifferences from Java’s method resolution.

Reference Types

Apart from primitives, everything else is an object and has an associated class defining its type.We’ll discuss classes, and class-related or class-like things like interfaces, traits and records shortly.

We might declare two variables, of type String and List, as follows:

String movie = 'The Matrix'List actors = ['Keanu Reeves', 'Hugo Weaving']
Generics

Groovy carries across the same concepts with regard to generics as Java.When defining classes and methods, it is possible to use a type parameter and createa generic class, interface, method or constructor.

Usage of generic classes and methods, regardless of whether they are defined in Javaor Groovy, may involve supplying a type argument.

We might declare a variable, of type"list of string", as follows:

List<String> roles = ['Trinity', 'Morpheus']

Java employs type erasure for backwards compatibility with earlier versions of Java.Dynamic Groovy can be thought of as more aggressively applying type erasure.In general, less generics type information will be checked at compile time.Groovy’s static nature employs similar checks to Java with regard to generics information.

1.4.2. Classes

Groovy classes are very similar to Java classes, and are compatible with Java ones at JVM level.They may have methods, fields and properties (think JavaBeans properties but with less boilerplate).Classes and class members can have the same modifiers (public, protected, private, static, etc.) as in Javawith some minor differences at the source level which are explained shortly.

The key differences between Groovy classes and their Java counterparts are:

  • Classes or methods with no visibility modifier are automatically public (a special annotation can be used to achieve package private visibility).

  • Fields with no visibility modifier are turned into properties automatically, which results in less verbose code,since explicit getter and setter methods aren’t needed. More on this aspect will be covered in thefields and properties section.

  • Classes do not need to have the same base name as their source file definitions but it is highly recommended in most scenarios (see also the next point about scripts).

  • One source file may contain one or more classes (but if a file contains any code not in a class, it is considered a script). Scripts are just classes with somespecial conventions and will have the same name as their source file (so don’t include a class definition within a script having the same name as the script source file).

The following code presents an example class.

class Person {(1)    String name(2)    Integer age    def increaseAge(Integer years) {(3)        this.age += years    }}
1class beginning, with the namePerson
2string field and property namedname
3method definition
Normal class

Normal classes refer to classes which are top level and concrete. This means they can be instantiated without restrictions from any other classes or scripts. This way, they can only be public (even though thepublic keyword may be suppressed). Classes are instantiated by calling their constructors, using thenew keyword, as in the following snippet.

def p = new Person()
Inner class

Inner classes are defined within another classes. The enclosing class can use the inner class as usual. On the other side, an inner class can access members of its enclosing class, even if they are private. Classes other than the enclosing class are not allowed to access inner classes. Here is an example:

class Outer {    private String privateStr    def callInnerMethod() {        new Inner().methodA()(1)    }    class Inner {(2)        def methodA() {            println "${privateStr}."(3)        }    }}
1the inner class is instantiated and its method gets called
2inner class definition, inside its enclosing class
3even being private, a field of the enclosing class is accessed by the inner class

There are some reasons for using inner classes:

  • They increase encapsulation by hiding the inner class from other classes, which do not need to know about it. This also leads to cleaner packages and workspaces.

  • They provide a good organization, by grouping classes that are used by only one class.

  • They lead to more maintainable codes, since inner classes are near the classes that use them.

It is common for an inner class to be an implementation of some interface whose method(s) are needed by the outer class.The code below illustrates this typical usage pattern, here being used with threads.

class Outer2 {    private String privateStr = 'some string'    def startThread() {       new Thread(new Inner2()).start()    }    class Inner2 implements Runnable {        void run() {            println "${privateStr}."        }    }}

Note that the classInner2 is defined only to provide an implementation of the methodrun to classOuter2.Anonymous inner classes help to eliminate verbosity in this case.That topic is covered shortly.

Groovy 3+ also supports Java syntax for non-static inner class instantiation, for example:

class Computer {    class Cpu {        int coreNumber        Cpu(int coreNumber) {            this.coreNumber = coreNumber        }    }}assert 4 == new Computer().new Cpu(4).coreNumber
Anonymous inner class

The earlier example of an inner class (Inner2) can be simplified with an anonymous inner class.The same functionality can be achieved with the following code:

class Outer3 {    private String privateStr = 'some string'    def startThread() {        new Thread(new Runnable() {(1)            void run() {                println "${privateStr}."            }        }).start()(2)    }}
1comparing with the last example of previous section, thenew Inner2() was replaced bynew Runnable() along with all its implementation
2the methodstart is invoked normally

Thus, there was no need to define a new class to be used just once.

Abstract class

Abstract classes represent generic concepts, thus, they cannot be instantiated, being created to be subclassed.Their members include fields/properties and abstract or concrete methods.Abstract methods do not have implementation, and must be implemented by concrete subclasses.

abstract class Abstract {(1)    String name    abstract def abstractMethod()(2)    def concreteMethod() {        println 'concrete'    }}
1abstract classes must be declared withabstract keyword
2abstract methods must also be declared withabstract keyword

Abstract classes are commonly compared to interfaces.There are at least two important differences of choosing one or another.First, while abstract classes may contain fields/properties and concrete methods, interfaces may contain only abstract methods (method signatures).Moreover, one class can implement several interfaces, whereas it can extend just one class, abstract or not.

Inheritance

Inheritance in Groovy resembles inheritance in Java.It provides a mechanism for a child class (or subclass) to reusecode or properties from a parent (or super class).Classes related through inheritance form an inheritance hierarchy.Common behavior and members are pushed up the hierarchy to reduce duplication.Specializations occur in child classes.

Different forms of inheritance are supported:

  • implementation inheritance where code (methods, fields or properties) from asuperclass or fromone or moretraits is reused by a child class

  • contract inheritance where a class promises to provide particular abstract methods defined in asuperclass,or defined in one or moretraits orinterfaces.

Superclasses

Parent classes share visible fields, properties or methods with child classes.A child class may have at most one parent class.Theextends keyword is used immediately prior to giving the superclass type.

Interfaces

An interface defines a contract that a class needs to conform to.An interface only defines a list of methods that needto be implemented, but does not define the method’s implementation.

interface Greeter {(1)    void greet(String name)(2)}
1an interface needs to be declared using theinterface keyword
2an interface only defines method signatures

Methods of an interface are alwayspublic. It is an error to useprotected orprivate methods in interfaces:

interface Greeter {    protected void greet(String name)(1)}
1Usingprotected is a compile-time error

A classimplements an interface if it defines the interface in itsimplements list or if any of its superclassesdoes:

class SystemGreeter implements Greeter {(1)    void greet(String name) {(2)        println "Hello $name"    }}def greeter = new SystemGreeter()assert greeter instanceof Greeter(3)
1TheSystemGreeter declares theGreeter interface using theimplements keyword
2Then implements the requiredgreet method
3Any instance ofSystemGreeter is also an instance of theGreeter interface

An interface can extend another interface:

interface ExtendedGreeter extends Greeter {(1)    void sayBye(String name)}
1theExtendedGreeter interface extends theGreeter interface using theextends keyword

It is worth noting that for a class to be an instance of an interface, it has to be explicit. For example, the followingclass defines thegreet method as it is declared in theGreeter interface, but does not declareGreeter in itsinterfaces:

class DefaultGreeter {    void greet(String name) { println "Hello" }}greeter = new DefaultGreeter()assert !(greeter instanceof Greeter)

In other words, Groovy does not define structural typing. It is however possible to make an instance of an objectimplement an interface at runtime, using theas coercion operator:

greeter = new DefaultGreeter()(1)coerced = greeter as Greeter(2)assert coerced instanceof Greeter(3)
1create an instance ofDefaultGreeter that does not implement the interface
2coerce the instance into aGreeter at runtime
3the coerced instance implements theGreeter interface

You can see that there are two distinct objects: one is the source object, aDefaultGreeter instance, which does notimplement the interface. The other is an instance ofGreeter that delegates to the coerced object.

Groovy interfaces do not support default implementation like Java 8 interfaces. If you are looking for somethingsimilar (but not equal),traits are close to interfaces, but allow default implementation as well as otherimportant features described in this manual.

1.4.3. Class members

Constructors

Constructors are special methods used to initialize an object with a specific state. As with normal methods,it is possible for a class to declare more than one constructor, so long as each constructor has a uniquetype signature. If an object doesn’t require any parameters during construction, it may use ano-arg constructor.If no constructors are supplied, an empty no-arg constructor will be provided by the Groovy compiler.

Groovy supports two invocation styles:

  • positional parameters are used in a similar to how you would use Java constructors

  • named parameters allow you to specify parameter names when invoking the constructor.

Positional parameters

To create an object by using positional parameters, the respective class needs to declare one or moreconstructors. In the case of multiple constructors, each must have a unique type signature. The constructors can alsobe added to the class using thegroovy.transform.TupleConstructor annotation.

Typically, once at least one constructor is declared, the class can only be instantiated by having one of itsconstructors called. It is worth noting that, in this case, you can’t normally create the class with named parameters.Groovy does support named parameters so long as the class contains a no-arg constructor or provides a constructor whichtakes aMap argument as the first (and potentially only) argument - see the next section for details.

There are three forms of using a declared constructor. The first one is the normal Java way, with thenew keyword.The others rely on coercion of lists into the desired types. In this case, it is possible to coerce with theaskeyword and by statically typing the variable.

class PersonConstructor {    String name    Integer age    PersonConstructor(name, age) {(1)        this.name = name        this.age = age    }}def person1 = new PersonConstructor('Marie', 1)(2)def person2 = ['Marie', 2] as PersonConstructor(3)PersonConstructor person3 = ['Marie', 3](4)
1Constructor declaration
2Constructor invocation, classic Java way
3Constructor usage, using coercion withas keyword
4Constructor usage, using coercion in assignment
Named parameters

If no (or a no-arg) constructor is declared, it is possible to create objects by passing parameters in the form of amap (property/value pairs). This can be in handy in cases where one wants to allow several combinations of parameters.Otherwise, by using traditional positional parameters it would be necessary to declare all possible constructors.Having a constructor where the first (and perhaps only) argument is aMap argument is also supported - such aconstructor may also be added using thegroovy.transform.MapConstructor annotation.

class PersonWOConstructor {(1)    String name    Integer age}def person4 = new PersonWOConstructor()(2)def person5 = new PersonWOConstructor(name: 'Marie')(3)def person6 = new PersonWOConstructor(age: 1)(4)def person7 = new PersonWOConstructor(name: 'Marie', age: 2)(5)
1No constructor declared
2No parameters given in the instantiation
3name parameter given in the instantiation
4age parameter given in the instantiation
5name andage parameters given in the instantiation

It is important to highlight, however, that this approach gives more power to the constructor caller,while imposing an increased responsibility on the caller to get the names and value types correct.Thus, if greater control is desired, declaring constructors using positional parameters might be preferred.

Notes:

  • While the example above supplied no constructor, you can also supply a no-arg constructoror a constructor where the first argument is aMap, most typically it’s the only argument.

  • When no (or a no-arg) constructor is declared, Groovy replaces the named constructor call by a callto the no-arg constructor followed by calls to the setter for each supplied named property.

  • When the first argument is a Map, Groovy combines all named parameters into a Map (regardless of ordering)and supplies the map as the first parameter. This can be a good approach if your properties are declared asfinal (since they will be set in the constructor rather than after the fact with setters).

  • You can support both named and positional constructionby supply both positional constructors as well as a no-arg or Map constructor.

  • You can support hybrid construction by having a constructor where the first argumentis a Map but there are also additional positional parameters. Use this style with caution.

Methods

Groovy methods are quite similar to other languages. Some peculiarities will be shown in the next subsections.

Method definition

A method is defined with a return type or with thedef keyword, to make the return type untyped. A method can also receive any number of arguments, which may not have their types explicitly declared. Java modifiers can be used normally, and if no visibility modifier is provided, the method is public.

Methods in Groovy always return some value. If noreturn statement is provided, the value evaluated in the last line executed will be returned. For instance, note that none of the following methods uses thereturn keyword.

def someMethod() { 'method called' }(1)String anotherMethod() { 'another method called' }(2)def thirdMethod(param1) { "$param1 passed" }(3)static String fourthMethod(String param1) { "$param1 passed" }(4)
1Method with no return type declared and no parameter
2Method with explicit return type and no parameter
3Method with a parameter with no type defined
4Static method with a String parameter
Named parameters

Like constructors, normal methods can also be called with named parameters.To support this notation, a convention is used where the first argument to the method is aMap.In the method body, the parameter values can be accessed as in normal maps (map.key).If the method has just a single Map argument, all supplied parameters must be named.

def foo(Map args) { "${args.name}: ${args.age}" }foo(name: 'Marie', age: 1)
Mixing named and positional parameters

Named parameters can be mixed with positional parameters.The same convention applies, in this case, in addition to theMap argument as the first argument,the method in question will have additional positional arguments as needed.Supplied positional parameters when calling the method must be in order.The named parameters can be in any position. They are grouped into the map and supplied asthe first parameter automatically.

def foo(Map args, Integer number) { "${args.name}: ${args.age}, and the number is ${number}" }foo(name: 'Marie', age: 1, 23)(1)foo(23, name: 'Marie', age: 1)(2)
1Method call with additionalnumber argument ofInteger type
2Method call with changed order of arguments

If we don’t have the Map as the first argument, then a Map must be supplied for that argument instead of named parameters.Failure to do so will lead togroovy.lang.MissingMethodException:

def foo(Integer number, Map args) { "${args.name}: ${args.age}, and the number is ${number}" }foo(name: 'Marie', age: 1, 23)(1)
1Method call throwsgroovy.lang.MissingMethodException: No signature of method: foo() is applicable for argument types: (LinkedHashMap, Integer) values: [[name:Marie, age:1], 23], because the named argumentMap parameter is not defined as the first argument

Above exception can be avoided if we replace named arguments with an explicitMap argument:

def foo(Integer number, Map args) { "${args.name}: ${args.age}, and the number is ${number}" }foo(23, [name: 'Marie', age: 1])(1)
1ExplicitMap argument in place of named arguments makes invocation valid
Although Groovy allows you to mix named and positional parameters, it can lead to unnecessary confusion.Mix named and positional arguments with caution.
Default arguments

Default arguments make parameters optional.If the argument is not supplied, the method assumes a default value.

def foo(String par1, Integer par2 = 1) { [name: par1, age: par2] }assert foo('Marie').age == 1

Parameters are dropped from the right, however mandatory parameters are never dropped.

def baz(a = 'a', int b, c = 'c', boolean d, e = 'e') { "$a $b $c $d $e" }assert baz(42, true) == 'a 42 c true e'assert baz('A', 42, true) == 'A 42 c true e'assert baz('A', 42, 'C', true) == 'A 42 C true e'assert baz('A', 42, 'C', true, 'E') == 'A 42 C true E'

The same rule applies to constructors as well as methods.If using@TupleConstructor, additional configuration options apply.

Varargs

Groovy supports methods with a variable number of arguments. They are defined like this:def foo(p1, …​, pn, T…​ args).Herefoo supportsn arguments by default, but also an unspecified number of further arguments exceedingn.

def foo(Object... args) { args.length }assert foo() == 0assert foo(1) == 1assert foo(1, 2) == 2

This example defines a methodfoo, that can take any number of arguments, including no arguments at all.args.length will return the number of arguments given. Groovy allowsT[] as an alternative notation toT…​.That means any method with an array as last parameter is seen by Groovy as a method that can take a variable number of arguments.

def foo(Object[] args) { args.length }assert foo() == 0assert foo(1) == 1assert foo(1, 2) == 2

If a method with varargs is called withnull as the vararg parameter, then the argument will benull and not an array of length one withnull as the only element.

def foo(Object... args) { args }assert foo(null) == null

If a varargs method is called with an array as an argument, then the argument will be that array instead of an array of length one containing the given array as the only element.

def foo(Object... args) { args }Integer[] ints = [1, 2]assert foo(ints) == [1, 2]

Another important point are varargs in combination with method overloading. In case of method overloading Groovy will select the most specific method.For example if a methodfoo takes a varargs argument of typeT and another methodfoo also takes one argument of typeT, the second method is preferred.

def foo(Object... args) { 1 }def foo(Object x) { 2 }assert foo() == 1assert foo(1) == 2assert foo(1, 2) == 1
Method selection algorithm

Dynamic Groovy supportsmultiple dispatch (aka multimethods).When calling a method, the actual method invoked is determineddynamically based on the run-time type of methods arguments.First the method name and number of arguments will be considered (including allowance for varargs),and then the type of each argument.Consider the following method definitions:

def method(Object o1, Object o2) { 'o/o' }def method(Integer i, String  s) { 'i/s' }def method(String  s, Integer i) { 's/i' }

Perhaps as expected, callingmethod withString andInteger parameters,invokes our third method definition.

assert method('foo', 42) == 's/i'

Of more interest here is when the types are not known at compile time.Perhaps the arguments are declared to be of typeObject (a list of such objects in our case).Java would determine that themethod(Object, Object) variant would be selected in allcases (unless casts were used) but as can be seen in the following example, Groovy uses the runtime typeand will invoke each of our methods once (and normally, no casting is needed):

List<List<Object>> pairs = [['foo', 1], [2, 'bar'], [3, 4]]assert pairs.collect { a, b -> method(a, b) } == ['s/i', 'i/s', 'o/o']

For each of the first two of our three method invocations an exact match of argument types was found.For the third invocation, an exact match ofmethod(Integer, Integer) wasn’t found butmethod(Object, Object)is still valid and will be selected.

Method selection then is about finding theclosest fit from valid method candidates which have compatibleparameter types.So,method(Object, Object) is also valid for the first two invocations but is not as close a matchas the variants where types exactly match.To determine the closest fit, the runtime has a notion of thedistance an actual argumenttype is away from the declared parameter type and tries to minimise the total distance across all parameters.

The following table illustrates some factors which affect the distance calculation.

AspectExample

Directly implemented interfaces match more closely than ones from further up the inheritance hierarchy.

Given these interface and method definitions:

interface I1 {}interface I2 extends I1 {}interface I3 {}class Clazz implements I3, I2 {}def method(I1 i1) { 'I1' }def method(I3 i3) { 'I3' }

The directly implemented interface will match:

assert method(new Clazz()) == 'I3'

An Object array is preferred over an Object.

def method(Object[] arg) { 'array' }def method(Object arg) { 'object' }assert method([] as Object[]) == 'array'

Non-vararg variants are favored over vararg variants.

def method(String s, Object... vargs) { 'vararg' }def method(String s) { 'non-vararg' }assert method('foo') == 'non-vararg'

If two vararg variants are applicable, the one which uses the minimum number of vararg arguments is preferred.

def method(String s, Object... vargs) { 'two vargs' }def method(String s, Integer i, Object... vargs) { 'one varg' }assert method('foo', 35, new Date()) == 'one varg'

Interfaces are preferred over super classes.

interface I {}class Base {}class Child extends Base implements I {}def method(Base b) { 'superclass' }def method(I i) { 'interface' }assert method(new Child()) == 'interface'

For a primitive argument type, a declared parameter type which is the same or slightly larger is preferred.

def method(Long l) { 'Long' }def method(Short s) { 'Short' }def method(BigInteger bi) { 'BigInteger' }assert method(35) == 'Long'

In the case where two variants have exactly the same distance, this is deemed ambiguous and will cause a runtime exception:

def method(Date d, Object o) { 'd/o' }def method(Object o, String s) { 'o/s' }def ex = shouldFail {    println method(new Date(), 'baz')}assert ex.message.contains('Ambiguous method overloading')

Casting can be used to select the desired method:

assert method(new Date(), (Object)'baz') == 'd/o'assert method((Object)new Date(), 'baz') == 'o/s'
Exception declaration

Groovy automatically allows you to treat checked exceptions like unchecked exceptions.This means that you don’t need to declare any checked exceptions that a method may throwas shown in the following example which can throw aFileNotFoundException if the file isn’t found:

def badRead() {    new File('doesNotExist.txt').text}shouldFail(FileNotFoundException) {    badRead()}

Nor will you be required to surround the call to thebadRead method in the previous example within a try/catchblock - though you are free to do so if you wish.

If you wish to declare any exceptions that your code might throw (checked or otherwise) you are free to do so.Adding exceptions won’t change how the code is used from any other Groovy code but can be seen as documentationfor the human reader of your code. The exceptions will become part of the method declaration in the bytecode,so if your code might be called from Java, it might be useful to include them.Using an explicit checked exception declaration is illustrated in the following example:

def badRead() throws FileNotFoundException {    new File('doesNotExist.txt').text}shouldFail(FileNotFoundException) {    badRead()}
Fields and Properties
Fields

A field is a member of a class, interface or trait which stores data.A field defined in a Groovy source file has:

  • a mandatoryaccess modifier (public,protected, orprivate)

  • one or more optionalmodifiers (static,final,synchronized)

  • an optionaltype

  • a mandatoryname

class Data {    private int id(1)    protected String description(2)    public static final boolean DEBUG = false(3)}
1aprivate field namedid, of typeint
2aprotected field nameddescription, of typeString
3apublic static final field namedDEBUG of typeboolean

A field may be initialized directly at declaration:

class Data {    private String id = IDGenerator.next()(1)    // ...}
1the private fieldid is initialized withIDGenerator.next()

It is possible to omit the type declaration of a field. This is however considered a bad practice and in general itis a good idea to use strong typing for fields:

class BadPractice {    private mapping(1)}class GoodPractice {    private Map<String,String> mapping(2)}
1the fieldmapping doesn’t declare a type
2the fieldmapping has a strong type

The difference between the two is important if you want to use optional type checking later.It is also important as a way to document the class design.However, in some cases like scripting or if you want to rely on duck typing it may be usefulto omit the type.

Properties

A property is an externally visible feature of a class. Rather than just using a public field to representsuch features (which provides a more limited abstraction and would restrict refactoring possibilities),the typical approach in Java is to follow the conventions outlined in theJavaBeans Specification, i.e. represent the property using acombination of a private backing field and getters/setters. Groovy follows these same conventionsbut provides a simpler way to define the property. You can define a property with:

  • anabsent access modifier (nopublic,protected orprivate)

  • one or more optionalmodifiers (static,final,synchronized)

  • an optionaltype

  • a mandatoryname

Groovy will then generate the getters/setters appropriately. For example:

class Person {    String name(1)    int age(2)}
1creates a backingprivate String name field, agetName and asetName method
2creates a backingprivate int age field, agetAge and asetAge method

If a property is declaredfinal, no setter is generated:

class Person {    final String name(1)    final int age(2)    Person(String name, int age) {        this.name = name(3)        this.age = age(4)    }}
1defines a read-only property of typeString
2defines a read-only property of typeint
3assigns thename parameter to thename field
4assigns theage parameter to theage field

Properties are accessed by name and will call the getter or setter transparently, unless the code is in the classwhich defines the property:

class Person {    String name    void name(String name) {        this.name = "Wonder $name"(1)    }    String title() {        this.name(2)    }}def p = new Person()p.name = 'Diana'(3)assert p.name == 'Diana'(4)p.name('Woman')(5)assert p.title() == 'Wonder Woman'(6)
1this.name will directly access the field because the property is accessed from within the class that defines it
2similarly a read access is done directly on thename field
3write access to the property is done outside of thePerson class so it will implicitly callsetName
4read access to the property is done outside of thePerson class so it will implicitly callgetName
5this will call thename method onPerson which performs a direct access to the field
6this will call thetitle method onPerson which performs a direct read access to the field

It is worth noting that this behavior of accessing the backing field directly is done in order to prevent a stackoverflow when using the property access syntax within a class that defines the property.

It is possible to list the properties of a class thanks to the metaproperties field of an instance:

class Person {    String name    int age}def p = new Person()assert p.properties.keySet().containsAll(['name','age'])

By convention, Groovy will recognize properties even if there is no backing fieldprovided there are getters or settersthat follow the Java Beans specification. For example:

class PseudoProperties {    // a pseudo property "name"    void setName(String name) {}    String getName() {}    // a pseudo read-only property "age"    int getAge() { 42 }    // a pseudo write-only property "groovy"    void setGroovy(boolean groovy) {  }}def p = new PseudoProperties()p.name = 'Foo'(1)assert p.age == 42(2)p.groovy = true(3)
1writingp.name is allowed because there is a pseudo-propertyname
2readingp.age is allowed because there is a pseudo-readonly propertyage
3writingp.groovy is allowed because there is a pseudo-write-only propertygroovy

This syntactic sugar is at the core of many DSLs written in Groovy.

Property naming conventions

It is generally recommended that the first two letters of a property name arelowercase and for multi-word properties that camel case is used.In those cases, generated getters and setters will have a name formed by capitalizing theproperty name and adding aget orset prefix (or optionally "is" for a boolean getter).So,getLength would be a getter for alength property andsetFirstName a setter for afirstName property.isEmpty might be the getter method name for a property namedempty.

Property names starting with a capital letter would have getters/setters with just the prefix added.So, the propertyFoo is allowed even though it isn’t following the recommended naming conventions.For this property, the accessor methods would besetFoo andgetFoo.A consequence of this is that you aren’t allowed to have both afoo and aFoo property,since they would have the same named accessor methods.

The JavaBeans specification makes a special case for properties which typically might be acronyms.If the first two letters of a property name are uppercase, no capitalization is performed(or more importantly, no decapitalization is done if generating the property name from the accessor method name).So,getURL would be the getter for aURL property.

Because of the special "acronym handling" property naming logic in the JavaBeans specification, theconversion to and from a property name are non-symmetrical. This leads to some strange edge cases.Groovy adopts a naming convention that avoids one ambiguity that might seem a little strange butwas popular at the time of Groovy’s design and has remained (so far) for historical reasons.Groovy looks at the second letter of a property name. If that is a capital, the property is deemed to beone of the acronym style properties and no capitalization is done, otherwise normal capitalization is done.Although wenever recommend it, it does allow you to have what might seem like "duplicate named" properties,e.g. you can haveaProp andAProp, orpNAME andPNAME. The getters would begetaProp andgetAProp,andgetpNAME andgetPNAME respectively.

Modifiers on a property

We have already seen that properties are defined by omitting the visibility modifier.In general, any other modifiers, e.g.transient would be copied across to the field.Two special cases are worth noting:

  • final, which we saw earlier is for read-only properties, is copied onto the backing field but also causes no setter to be defined

  • static is copied onto the backing field but also causes the accessor methods to be static

If you wish to have a modifier likefinal also carried over to the accessor methods,you can write your properties long hand or consider using asplit property definition.

Annotations on a property

Annotations, including those associated with AST transforms,are copied on to the backing field for the property.This allows AST transforms which are applicable to fields tobe applied to properties, e.g.:

class Animal {    int lowerCount = 0    @Lazy String name = { lower().toUpperCase() }()    String lower() { lowerCount++; 'sloth' }}def a = new Animal()assert a.lowerCount == 0(1)assert a.name == 'SLOTH'(2)assert a.lowerCount == 1(3)
1Confirms no eager initialization
2Normal property access
3Confirms initialization upon property access
Split property definition with an explicit backing field

Groovy’s property syntax is a convenient shorthand when your class designfollows certain conventions which align with common JavaBean practice.If your class doesn’t exactly fit these conventions,you can certainly write the getter, setter and backing field long hand like you would in Java.However, Groovy does provide a split definition capability which still providesa shortened syntax while allowing slight adjustments to the conventions.For a split definition, you write a field and a property with the same name and type.Only one of the field or property may have an initial value.

For split properties, annotations on the field remain on the backing field for the property.Annotations on the property part of the definition are copied onto the getter and setter methods.

This mechanism allows a number of common variations that property users may wishto use if the standard property definition doesn’t exactly fit their needs.For example, if the backing field should beprotected rather thanprivate:

class HasPropertyWithProtectedField {    protected String name(1)    String name(2)}
1Protected backing field for name property instead of normal private one
2Declare name property

Or, the same example but with a package-private backing field:

class HasPropertyWithPackagePrivateField {    String name(1)    @PackageScope String name(2)}
1Declare name property
2Package-private backing field for name property instead of normal private one

As a final example, we may wish to apply method-related AST transforms,or in general, any annotation to the setters/getters,e.g. to have the accessors be synchronized:

class HasPropertyWithSynchronizedAccessorMethods {    private String name(1)    @Synchronized String name(2)}
1Backing field for name property
2Declare name property with annotation for setter/getter
Explicit accessor methods

The automatic generation of accessor methods doesn’t occur if thereis an explicit definition of the getter or setter in the class.This allows you to modify the normal behavior of such a getter or setter if needed.Inherited accessor methods aren’t normally considered but if an inheritedaccessor method is marked final, that will also cause no generation of anadditional accessor method to honor thefinal requirement of no subclassing of such methods.

1.4.4. Annotations

Annotation definition

An annotation is a kind of special interface dedicated at annotating elements of the code. An annotation is a type whichsuperinterface is thejava.lang.annotation.Annotation interface. Annotations are declared in a verysimilar way to interfaces, using the@interface keyword:

@interface SomeAnnotation {}

An annotation may define members in the form of methods without bodies and an optional default value. The possiblemember types are limited to:

For example:

@interface SomeAnnotation {    String value()(1)}@interface SomeAnnotation {    String value() default 'something'(2)}@interface SomeAnnotation {    int step()(3)}@interface SomeAnnotation {    Class appliesTo()(4)}@interface SomeAnnotation {}@interface SomeAnnotations {    SomeAnnotation[] value()(5)}enum DayOfWeek { mon, tue, wed, thu, fri, sat, sun }@interface Scheduled {    DayOfWeek dayOfWeek()(6)}
1an annotation defining avalue member of typeString
2an annotation defining avalue member of typeString with a default value ofsomething
3an annotation defining astep member of type the primitive typeint
4an annotation defining aappliesTo member of typeClass
5an annotation defining avalue member which type is an array of another annotation type
6an annotation defining adayOfWeek member which type is the enumeration typeDayOfWeek

Unlike in the Java language, in Groovy, an annotation can be used to alter the semantics of the language. It is especiallytrue of AST transformations which will generate code based on annotations.

Annotation placement

An annotation can be applied on various elements of the code:

@SomeAnnotation(1)void someMethod() {    // ...}@SomeAnnotation(2)class SomeClass {}@SomeAnnotation String var(3)
1@SomeAnnotation applies to thesomeMethod method
2@SomeAnnotation applies to theSomeClass class
3@SomeAnnotation applies to thevar variable

In order to limit the scope where an annotation can be applied, it is necessary to declare it on the annotationdefinition, using thejava.lang.annotation.Target annotation. For example, here is how you woulddeclare that an annotation can be applied to a class or a method:

import java.lang.annotation.ElementTypeimport java.lang.annotation.Target@Target([ElementType.METHOD, ElementType.TYPE])(1)@interface SomeAnnotation {}(2)
1the@Target annotation is meant to annotate an annotation with a scope.
2@SomeAnnotation will therefore only be allowed onTYPE orMETHOD

The list of possible targets is available in thejava.lang.annotation.ElementType.

Groovy does not support thejava.lang.annotation.ElementType#TYPE_PARAMETER andjava.lang.annotation.ElementType#TYPE_PARAMETER element types which were introduced in Java 8.
Annotation member values

When an annotation is used, it is required to set at least all members that do not have a default value. For example:

@interface Page {    int statusCode()}@Page(statusCode=404)void notFound() {    // ...}

However it is possible to omitvalue= in the declaration of the value of an annotation if the membervalue is theonly one being set:

@interface Page {    String value()    int statusCode() default 200}@Page(value='/home')(1)void home() {    // ...}@Page('/users')(2)void userList() {    // ...}@Page(value='error',statusCode=404)(3)void notFound() {    // ...}
1we can omit thestatusCode because it has a default value, butvalue needs to be set
2sincevalue is the only mandatory member without a default, we can omitvalue=
3if bothvalue andstatusCode need to be set, it is required to usevalue= for the defaultvalue member
Retention policy

The visibility of an annotation depends on its retention policy. The retention policy of an annotation is set usingthejava.lang.annotation.Retention annotation:

import java.lang.annotation.Retentionimport java.lang.annotation.RetentionPolicy@Retention(RetentionPolicy.SOURCE)(1)@interface SomeAnnotation {}(2)
1the@Retention annotation annotates the@SomeAnnotation annotation
2so@SomeAnnotation will have aSOURCE retention

The list of possible retention targets and description is available in thejava.lang.annotation.RetentionPolicy enumeration. Thechoice usually depends on whether you want an annotation to be visible atcompile time or runtime.

Closure annotation parameters

An interesting feature of annotations in Groovy is that you can use a closure as an annotation value. Thereforeannotations may be used with a wide variety of expressions and still have IDE support. For example, imagine aframework where you want to execute some methods based on environmental constraints like the JDK version or the OS.One could write the following code:

class Tasks {    Set result = []    void alwaysExecuted() {        result << 1    }    @OnlyIf({ jdk>=6 })    void supportedOnlyInJDK6() {        result << 'JDK 6'    }    @OnlyIf({ jdk>=7 && windows })    void requiresJDK7AndWindows() {        result << 'JDK 7 Windows'    }}

For the@OnlyIf annotation to accept aClosure as an argument, you only have to declare thevalue as aClass:

@Retention(RetentionPolicy.RUNTIME)@interface OnlyIf {    Class value()(1)}

To complete the example, let’s write a sample runner that would use that information:

class Runner {    static <T> T run(Class<T> taskClass) {        def tasks = taskClass.newInstance()(1)        def params = [jdk: 6, windows: false](2)        tasks.class.declaredMethods.each { m ->(3)            if (Modifier.isPublic(m.modifiers) && m.parameterTypes.length == 0) {(4)                def onlyIf = m.getAnnotation(OnlyIf)(5)                if (onlyIf) {                    Closure cl = onlyIf.value().newInstance(tasks,tasks)(6)                    cl.delegate = params(7)                    if (cl()) {(8)                        m.invoke(tasks)(9)                    }                } else {                    m.invoke(tasks)(10)                }            }        }        tasks(11)    }}
1create a new instance of the class passed as an argument (the task class)
2emulate an environment which is JDK 6 and not Windows
3iterate on all declared methods of the task class
4if the method is public and takes no-argument
5try to find the@OnlyIf annotation
6if it is found get thevalue and create a newClosure out of it
7set thedelegate of the closure to our environment variable
8call the closure, which is the annotation closure. It will return aboolean
9if it istrue, call the method
10if the method is not annotated with@OnlyIf, execute the method anyway
11after that, return the task object

Then the runner can be used this way:

def tasks = Runner.run(Tasks)assert tasks.result == [1, 'JDK 6'] as Set
Meta-annotations
Declaring meta-annotations

Meta-annotations, also known as annotation aliases are annotations thatare replaced at compile time by other annotations (one meta-annotationis an alias for one or more annotations). Meta-annotations can be used toreduce the size of code involving multiple annotations.

Let’s start with a simple example. Imagine you have the @Serviceand @Transactional annotations and that you want to annotate a classwith both:

@Service@Transactionalclass MyTransactionalService {}

Given the multiplication of annotations that you could add to the same class, a meta-annotationcould help by reducing the two annotations with a single one having the very same semantics. For example,we might want to write this instead:

@TransactionalService(1)class MyTransactionalService {}
1@TransactionalService is a meta-annotation

A meta-annotation is declared as a regular annotation but annotated with@AnnotationCollector and thelist of annotations it is collecting. In our case, the@TransactionalService annotation can be written:

import groovy.transform.AnnotationCollector@Service(1)@Transactional(2)@AnnotationCollector(3)@interface TransactionalService {}
1annotate the meta-annotation with@Service
2annotate the meta-annotation with@Transactional
3annotate the meta-annotation with@AnnotationCollector
Behavior of meta-annotations

Groovy supports bothprecompiled andsource formmeta-annotations. This means that your meta-annotation may beprecompiled, or you can have it in the same source tree as the one youare currently compiling.

INFO: Meta-annotations are a Groovy-only feature. There isno chance for you to annotate a Java class with a meta-annotation andhope it will do the same as in Groovy. Likewise, you cannot write ameta-annotation in Java: both the meta-annotation definition and usagehave to be Groovy code. But you can happily collect Java annotationsand Groovy annotations within your meta-annotation.

When the Groovy compiler encounters a class annotated with ameta-annotation, it replaces it with the collected annotations. So,in our previous example, it willreplace @TransactionalService with @Transactional and @Service:

def annotations = MyTransactionalService.annotations*.annotationType()assert (Service in annotations)assert (Transactional in annotations)

The conversion from a meta-annotation to the collected annotations is performed during thesemantic analysis compilation phase. 

In addition to replacing the alias with the collected annotations, a meta-annotation is capable ofprocessing them, including arguments.

Meta-annotation parameters

Meta-annotations can collect annotations which have parameters. To illustrate this,we will imagine two annotations, each of them accepting one argument:

@Timeout(after=3600)@Dangerous(type='explosive')

And suppose that you want to create a meta-annotation named @Explosive:

@Timeout(after=3600)@Dangerous(type='explosive')@AnnotationCollectorpublic @interface Explosive {}

By default, when the annotations are replaced, they will get theannotation parameter valuesas they were defined in the alias. More interesting,the meta-annotation supports overriding specific values:

@Explosive(after=0)(1)class Bomb {}
1theafter value provided as a parameter to @Explosive overrides the one defined in the@Timeout annotation

If two annotations define the same parameter name, the default processorwill copy the annotation value to all annotations that accept this parameter:

@Retention(RetentionPolicy.RUNTIME)public @interface Foo {   String value()(1)}@Retention(RetentionPolicy.RUNTIME)public @interface Bar {    String value()(2)}@Foo@Bar@AnnotationCollectorpublic @interface FooBar {}(3)@Foo('a')@Bar('b')class Bob {}(4)assert Bob.getAnnotation(Foo).value() == 'a'(5)println Bob.getAnnotation(Bar).value() == 'b'(6)@FooBar('a')class Joe {}(7)assert Joe.getAnnotation(Foo).value() == 'a'(8)println Joe.getAnnotation(Bar).value() == 'a'(9)
1the@Foo annotation defines thevalue member of typeString
2the@Bar annotation also defines thevalue member of typeString
3the@FooBar meta-annotation aggregates@Foo and@Bar
4classBob is annotated with@Foo and@Bar
5the value of the@Foo annotation onBob isa
6while the value of the@Bar annotation onBob isb
7classJoe is annotated with@FooBar
8then the value of the@Foo annotation onJoe isa
9and the value of the@Bar annotation onJoe is alsoa

In the second case, the meta-annotation value was copied inboth@Foo and@Bar annotations.

It is a compile time error if the collected annotations define the same memberswith incompatible types. For example if on the previous example@Foo defined a value oftypeString but@Bar defined a value of typeint.

It is however possible to customize the behavior of meta-annotations and describe how collectedannotations are expanded. We’ll look at how to do that shortly but first there is an advancedprocessing option to cover.

Handling duplicate annotations in meta-annotations

The@AnnotationCollector annotation supports amode parameter which can be used toalter how the default processor handles annotation replacement in the presence ofduplicate annotations.

INFO: Custom processors (discussed next) may or may not support this parameter.

As an example, suppose you create a meta-annotation containing the@ToString annotationand then place your meta-annotation on a class that already has an explicit@ToStringannotation. Should this be an error? Should both annotations be applied? Does one takepriority over the other? There is no correct answer. In some scenarios it might bequite appropriate for any of these answers to be correct. So, rather than trying topreempt one correct way to handle the duplicate annotation issue, Groovy lets youwrite your own custom meta-annotation processors (covered next) and lets you writewhatever checking logic you like within AST transforms - which are a frequent target foraggregating. Having said that, by simply setting themode, a number of commonlyexpected scenarios are handled automatically for you within any extra coding.The behavior of themode parameter is determined by theAnnotationCollectorModeenum value chosen and is summarized in the following table.

Mode

Description

DUPLICATE

Annotations from the annotation collection will always be inserted. After all transforms have been run, it will be an error if multiple annotations (excluding those with SOURCE retention) exist.

PREFER_COLLECTOR

Annotations from the collector will be added and any existing annotations with the same name will be removed.

PREFER_COLLECTOR_MERGED

Annotations from the collector will be added and any existing annotations with the same name will be removed but any new parameters found within existing annotations will be merged into the added annotation.

PREFER_EXPLICIT

Annotations from the collector will be ignored if any existing annotations with the same name are found.

PREFER_EXPLICIT_MERGED

Annotations from the collector will be ignored if any existing annotations with the same name are found but any new parameters on the collector annotation will be added to existing annotations.

Custom meta-annotation processors

A custom annotation processor will let you choose how to expand ameta-annotation into collected annotations. The behaviour of the meta-annotation is,in this case, totally up to you. To do this, you must:

To illustrate this, we are going to explore how the meta-annotation@CompileDynamic is implemented.

@CompileDynamic is a meta-annotation that expands itselfto @CompileStatic(TypeCheckingMode.SKIP). The problem is that thedefault meta annotation processor doesn’t support enums and theannotation valueTypeCheckingMode.SKIP is one.

The naive implementation here would not work:

@CompileStatic(TypeCheckingMode.SKIP)@AnnotationCollectorpublic @interface CompileDynamic {}

Instead, we will define it like this:

@AnnotationCollector(processor = "org.codehaus.groovy.transform.CompileDynamicProcessor")public @interface CompileDynamic {}

The first thing you may notice is that our interface is no longerannotated with @CompileStatic. The reason for this is that we rely ontheprocessor parameter instead, that references a class whichwill generate the annotation.

Here is how the custom processor is implemented:

CompileDynamicProcessor.groovy
@CompileStatic(1)class CompileDynamicProcessor extends AnnotationCollectorTransform {(2)    private static final ClassNode CS_NODE = ClassHelper.make(CompileStatic)(3)    private static final ClassNode TC_NODE = ClassHelper.make(TypeCheckingMode)(4)    List<AnnotationNode> visit(AnnotationNode collector,(5)                               AnnotationNode aliasAnnotationUsage,(6)                               AnnotatedNode aliasAnnotated,(7)                               SourceUnit source) {(8)        def node = new AnnotationNode(CS_NODE)(9)        def enumRef = new PropertyExpression(            new ClassExpression(TC_NODE), "SKIP")(10)        node.addMember("value", enumRef)(11)        Collections.singletonList(node)(12)    }}
1our custom processor is written in Groovy, and for better compilation performance, we use static compilation
2the custom processor has to extendorg.codehaus.groovy.transform.AnnotationCollectorTransform
3create a class node representing the@CompileStatic annotation type
4create a class node representing theTypeCheckingMode enum type
5collector is the@AnnotationCollector node found in the meta-annotation. Usually unused.
6aliasAnnotationUsage is the meta-annotation being expanded, here it is@CompileDynamic
7aliasAnnotated is the node being annotated with the meta-annotation
8sourceUnit is theSourceUnit being compiled
9we create a new annotation node for@CompileStatic
10we create an expression equivalent toTypeCheckingMode.SKIP
11we add that expression to the annotation node, which is now@CompileStatic(TypeCheckingMode.SKIP)
12return the generated annotation

In the example, thevisit method is the only method which has to be overridden. It is meant to return a list ofannotation nodes that will be added to the node annotated with the meta-annotation. In this example, we return asingle one corresponding to@CompileStatic(TypeCheckingMode.SKIP).

1.4.5. Traits

Traits are a structural construct of the language which allows:

  • composition of behaviors

  • runtime implementation of interfaces

  • behavior overriding

  • compatibility with static type checking/compilation

They can be seen asinterfaces carrying bothdefault implementations andstate. A trait is defined using thetrait keyword:

trait FlyingAbility {(1)        String fly() { "I'm flying!" }(2)}
1declaration of a trait
2declaration of a method inside a trait

Then it can be used like a normal interface using theimplements keyword:

class Bird implements FlyingAbility {}(1)def b = new Bird()(2)assert b.fly() == "I'm flying!"(3)
1Adds the traitFlyingAbility to theBird class capabilities
2instantiate a newBird
3theBird class automatically gets the behavior of theFlyingAbility trait

Traits allow a wide range of capabilities, from simple composition to testing, which are described thoroughly in this section.

Methods
Public methods

Declaring a method in a trait can be done like any regular method in a class:

trait FlyingAbility {(1)        String fly() { "I'm flying!" }(2)}
1declaration of a trait
2declaration of a method inside a trait
Abstract methods

In addition, traits may declareabstract methods too, which therefore need to be implemented in the class implementing the trait:

trait Greetable {    abstract String name()(1)    String greeting() { "Hello, ${name()}!" }(2)}
1implementing class will have to declare thename method
2can be mixed with a concrete method

Then the trait can be used like this:

class Person implements Greetable {(1)    String name() { 'Bob' }(2)}def p = new Person()assert p.greeting() == 'Hello, Bob!'(3)
1implement the traitGreetable
2sincename was abstract, it is required to implement it
3thengreeting can be called
Private methods

Traits may also define private methods. Those methods will not appear in the trait contract interface:

trait Greeter {    private String greetingMessage() {(1)        'Hello from a private method!'    }    String greet() {        def m = greetingMessage()(2)        println m        m    }}class GreetingMachine implements Greeter {}(3)def g = new GreetingMachine()assert g.greet() == "Hello from a private method!"(4)try {    assert g.greetingMessage()(5)} catch (MissingMethodException e) {    println "greetingMessage is private in trait"}
1define a private methodgreetingMessage in the trait
2the publicgreet message callsgreetingMessage by default
3create a class implementing the trait
4greet can be called
5but notgreetingMessage
Traits only supportpublic andprivate methods. Neitherprotected norpackage private scopes aresupported.
Final methods

If we have a class implementing a trait, conceptually implementations from the trait methodsare "inherited" into the class. But, in reality, there is no base class containing suchimplementations. Rather, they are woven directly into the class. A final modifier on a methodjust indicates what the modifier will be for the woven method. While it would likely beconsidered bad style to inherit and override or multiply inherit methods with the samesignature but a mix of final and non-final variants, Groovy doesn’t prohibit this scenario.Normal method selection applies and the modifier used will be determined from the resulting method.You might consider creating a base class which implements the desired trait(s) if youwant trait implementation methods that can’t be overridden.

The meaning of this

this represents the implementing instance. Think of a trait as a superclass. This means that when you write:

trait Introspector {    def whoAmI() { this }}class Foo implements Introspector {}def foo = new Foo()

then calling:

foo.whoAmI()

will return the same instance:

assert foo.whoAmI().is(foo)
Interfaces

Traits may implement interfaces, in which case the interfaces are declared using theimplements keyword:

interface Named {(1)    String name()}trait Greetable implements Named {(2)    String greeting() { "Hello, ${name()}!" }}class Person implements Greetable {(3)    String name() { 'Bob' }(4)}def p = new Person()assert p.greeting() == 'Hello, Bob!'(5)assert p instanceof Named(6)assert p instanceof Greetable(7)
1declaration of a normal interface
2addNamed to the list of implemented interfaces
3declare a class that implements theGreetable trait
4implement the missingname method
5thegreeting implementation comes from the trait
6make surePerson implements theNamed interface
7make surePerson implements theGreetable trait
Properties

A trait may define properties, like in the following example:

trait Named {    String name(1)}class Person implements Named {}(2)def p = new Person(name: 'Bob')(3)assert p.name == 'Bob'(4)assert p.getName() == 'Bob'(5)
1declare a propertyname inside a trait
2declare a class which implements the trait
3the property is automatically made visible
4it can be accessed using the regular property accessor
5or using the regular getter syntax
Fields
Private fields

Since traits allow the use of private methods, it can also be interesting to use private fields to store state. Traitswill let you do that:

trait Counter {    private int count = 0(1)    int count() { count += 1; count }(2)}class Foo implements Counter {}(3)def f = new Foo()assert f.count() == 1(4)assert f.count() == 2
1declare a private fieldcount inside a trait
2declare a public methodcount that increments the counter and returns it
3declare a class that implements theCounter trait
4thecount method can use the private field to keep state
This is a major difference withJava 8 virtual extension methods. While virtual extension methodsdo not carry state, traits can. Moreover, traits in Groovy are supported starting with Java 6, because their implementation does not rely on virtual extension methods. Thismeans that even if a trait can be seen from a Java class as a regular interface, that interface willnot have default methods, only abstract ones.
Public fields

Public fields work the same way as private fields, but in order to avoid thediamond problem,field names are remapped in the implementing class:

trait Named {    public String name(1)}class Person implements Named {}(2)def p = new Person()(3)p.Named__name = 'Bob'(4)
1declare a publicfield inside the trait
2declare a class implementing the trait
3create an instance of that class
4the public field is available, but renamed

The name of the field depends on the fully qualified name of the trait. All dots (.) in package are replaced with an underscore (_), and the final name includes a double underscore.So if the type of the field isString, the name of the package ismy.package, the name of the trait isFoo and the name of the field isbar,in the implementing class, the public field will appear as:

String my_package_Foo__bar
While traits support public fields, it is not recommended to use them and considered as a bad practice.
Composition of behaviors

Traits can be used to implement multiple inheritance in a controlled way. For example, we can have the following traits:

trait FlyingAbility {(1)        String fly() { "I'm flying!" }(2)}trait SpeakingAbility {    String speak() { "I'm speaking!" }}

And a class implementing both traits:

class Duck implements FlyingAbility, SpeakingAbility {}(1)def d = new Duck()(2)assert d.fly() == "I'm flying!"(3)assert d.speak() == "I'm speaking!"(4)
1theDuck class implements bothFlyingAbility andSpeakingAbility
2creates a new instance ofDuck
3we can call the methodfly fromFlyingAbility
4but also the methodspeak fromSpeakingAbility

Traits encourage the reuse of capabilities among objects, and the creation of new classes by the composition of existing behavior.

Overriding default methods

Traits provide default implementations for methods, but it is possible to override them in the implementing class. For example, wecan slightly change the example above, by having a duck which quacks:

class Duck implements FlyingAbility, SpeakingAbility {    String quack() { "Quack!" }(1)    String speak() { quack() }(2)}def d = new Duck()assert d.fly() == "I'm flying!"(3)assert d.quack() == "Quack!"(4)assert d.speak() == "Quack!"(5)
1define a method specific toDuck, namedquack
2override the default implementation ofspeak so that we usequack instead
3the duck is still flying, from the default implementation
4quack comes from theDuck class
5speak no longer uses the default implementation fromSpeakingAbility
Extending traits
Simple inheritance

Traits may extend another trait, in which case you must use theextends keyword:

trait Named {    String name(1)}trait Polite extends Named {(2)    String introduce() { "Hello, I am $name" }(3)}class Person implements Polite {}def p = new Person(name: 'Alice')(4)assert p.introduce() == 'Hello, I am Alice'(5)
1theNamed trait defines a singlename property
2thePolite traitextends theNamed trait
3Polite adds a new method which has access to thename property of the super-trait
4thename property is visible from thePerson class implementingPolite
5as is theintroduce method
Multiple inheritance

Alternatively, a trait may extend multiple traits. In that case, all super traits must be declared in theimplementsclause:

trait WithId {(1)    Long id}trait WithName {(2)    String name}trait Identified implements WithId, WithName {}(3)
1WithId trait defines theid property
2WithName trait defines thename property
3Identified is a trait which inherits bothWithId andWithName
Duck typing and traits
Dynamic code

Traits can call any dynamic code, like a normal Groovy class. This means that you can, in the body of a method, callmethods which are supposed to exist in an implementing class, without having to explicitly declare them in an interface.This means that traits are fully compatible with duck typing:

trait SpeakingDuck {    String speak() { quack() }(1)}class Duck implements SpeakingDuck {    String methodMissing(String name, args) {        "${name.capitalize()}!"(2)    }}def d = new Duck()assert d.speak() == 'Quack!'(3)
1theSpeakingDuck expects thequack method to be defined
2theDuck class does implement the method usingmethodMissing
3calling thespeak method triggers a call toquack which is handled bymethodMissing
Dynamic methods in a trait

It is also possible for a trait to implement MOP methods likemethodMissing orpropertyMissing, in which case implementing classeswill inherit the behavior from the trait, like in this example:

trait DynamicObject {(1)    private Map props = [:]    def methodMissing(String name, args) {        name.toUpperCase()    }    def propertyMissing(String name) {        props.get(name)    }    void setProperty(String name, Object value) {        props.put(name, value)    }}class Dynamic implements DynamicObject {    String existingProperty = 'ok'(2)    String existingMethod() { 'ok' }(3)}def d = new Dynamic()assert d.existingProperty == 'ok'(4)assert d.foo == null(5)d.foo = 'bar'(6)assert d.foo == 'bar'(7)assert d.existingMethod() == 'ok'(8)assert d.someMethod() == 'SOMEMETHOD'(9)
1create a trait implementing several MOP methods
2theDynamic class defines a property
3theDynamic class defines a method
4calling an existing property will call the method fromDynamic
5calling a non-existing property will call the method from the trait
6will callsetProperty defined on the trait
7will callgetProperty defined on the trait
8calling an existing method onDynamic
9but calling a non-existing method thanks to the traitmethodMissing
Multiple inheritance conflicts
Default conflict resolution

It is possible for a class to implement multiple traits. If some trait defines a method with the same signature as amethod in another trait, we have a conflict:

trait A {    String exec() { 'A' }(1)}trait B {    String exec() { 'B' }(2)}class C implements A,B {}(3)
1traitA defines a method namedexec returning aString
2traitB defines the very same method
3classC implements both traits

In this case, the default behavior is that the method from thelast declared trait in theimplements clause wins.Here,B is declared afterA so the method fromB will be picked up:

def c = new C()assert c.exec() == 'B'
User conflict resolution

In case this behavior is not the one you want, you can explicitly choose which method to call using theTrait.super.foo syntax.In the example above, we can ensure the method from trait A is invoked by writing this:

class C implements A,B {    String exec() { A.super.exec() }(1)}def c = new C()assert c.exec() == 'A'(2)
1explicit call ofexec from the traitA
2calls the version fromA instead of using the default resolution, which would be the one fromB
Runtime implementation of traits
Implementing a trait at runtime

Groovy also supports implementing traits dynamically at runtime. It allows you to "decorate" an existing object using atrait. As an example, let’s start with this trait and the following class:

trait Extra {    String extra() { "I'm an extra method" }(1)}class Something {(2)    String doSomething() { 'Something' }(3)}
1theExtra trait defines anextra method
2theSomething class doesnot implement theExtra trait
3Something only defines a methoddoSomething

Then if we do:

def s = new Something()s.extra()

the call to extra would fail becauseSomething is not implementingExtra. It is possible to do it at runtime withthe following syntax:

def s = new Something() as Extra(1)s.extra()(2)s.doSomething()(3)
1use of theas keyword to coerce an object to a traitat runtime
2thenextra can be called on the object
3anddoSomething is still callable
When coercing an object to a trait, the result of the operation is not the same instance. It is guaranteedthat the coerced object will implement both the traitand the interfaces that the original object implements, butthe result willnot be an instance of the original class.
Implementing multiple traits at once

Should you need to implement several traits at once, you can use thewithTraits method instead of theas keyword:

trait A { void methodFromA() {} }trait B { void methodFromB() {} }class C {}def c = new C()c.methodFromA()(1)c.methodFromB()(2)def d = c.withTraits A, B(3)d.methodFromA()(4)d.methodFromB()(5)
1call tomethodFromA will fail becauseC doesn’t implementA
2call tomethodFromB will fail becauseC doesn’t implementB
3withTrait will wrapc into something which implementsA andB
4methodFromA will now pass becaused implementsA
5methodFromB will now pass becaused also implementsB
When coercing an object to multiple traits, the result of the operation is not the same instance. It is guaranteedthat the coerced object will implement both the traitsand the interfaces that the original object implements, butthe result willnot be an instance of the original class.
Chaining behavior

Groovy supports the concept ofstackable traits. The idea is to delegate from one trait to the other if the current traitis not capable of handling a message. To illustrate this, let’s imagine a message handler interface like this:

interface MessageHandler {    void on(String message, Map payload)}

Then you can compose a message handler by applying small behaviors. For example, let’s define a default handler in theform of a trait:

trait DefaultHandler implements MessageHandler {    void on(String message, Map payload) {        println "Received $message with payload $payload"    }}

Then any class can inherit the behavior of the default handler by implementing the trait:

class SimpleHandler implements DefaultHandler {}

Now what if you want to log all messages, in addition to the default handler? One option is to write this:

class SimpleHandlerWithLogging implements DefaultHandler {    void on(String message, Map payload) {(1)        println "Seeing $message with payload $payload"(2)        DefaultHandler.super.on(message, payload)(3)    }}
1explicitly implement theon method
2perform logging
3continue by delegating to theDefaultHandler trait

This works but this approach has drawbacks:

  1. the logging logic is bound to a "concrete" handler

  2. we have an explicit reference toDefaultHandler in theon method, meaning that if we happen to change the trait that our class implements, code will be broken

As an alternative, we can write another trait which responsibility is limited to logging:

trait LoggingHandler implements MessageHandler {(1)    void on(String message, Map payload) {        println "Seeing $message with payload $payload"(2)        super.on(message, payload)(3)    }}
1the logging handler is itself a handler
2prints the message it receives
3thensuper makes it delegate the call to the next trait in the chain

Then our class can be rewritten as this:

class HandlerWithLogger implements DefaultHandler, LoggingHandler {}def loggingHandler = new HandlerWithLogger()loggingHandler.on('test logging', [:])

which will print:

Seeing test logging with payload [:]Received test logging with payload [:]

As the priority rules imply thatLoggerHandler wins because it is declared last, then a call toon will usethe implementation fromLoggingHandler. But the latter has a call tosuper, which means the next trait in thechain. Here, the next trait isDefaultHandler soboth will be called:

The interest of this approach becomes more evident if we add a third handler, which is responsible for handling messagesthat start withsay:

trait SayHandler implements MessageHandler {    void on(String message, Map payload) {        if (message.startsWith("say")) {(1)            println "I say ${message - 'say'}!"        } else {            super.on(message, payload)(2)        }    }}
1a handler specific precondition
2if the precondition is not met, pass the message to the next handler in the chain

Then our final handler looks like this:

class Handler implements DefaultHandler, SayHandler, LoggingHandler {}def h = new Handler()h.on('foo', [:])h.on('sayHello', [:])

Which means:

  • messages will first go through the logging handler

  • the logging handler callssuper which will delegate to the next handler, which is theSayHandler

  • if the message starts withsay, then the handler consumes the message

  • if not, thesay handler delegates to the next handler in the chain

This approach is very powerful because it allows you to write handlers that do not know each other and yet let youcombine them in the order you want. For example, if we execute the code, it will print:

Seeing foo with payload [:]Received foo with payload [:]Seeing sayHello with payload [:]I say Hello!

but if we move the logging handler to be the second one in the chain, the output is different:

class AlternateHandler implements DefaultHandler, LoggingHandler, SayHandler {}h = new AlternateHandler()h.on('foo', [:])h.on('sayHello', [:])

prints:

Seeing foo with payload [:]Received foo with payload [:]I say Hello!

The reason is that now, since theSayHandler consumes the message without callingsuper, the logging handler isnot called anymore.

Semantics of super inside a trait

If a class implements multiple traits and a call to an unqualifiedsuper is found, then:

  1. if the class implements another trait, the call delegates to the next trait in the chain

  2. if there isn’t any trait left in the chain,super refers to the super class of the implementing class (this)

For example, it is possible to decorate final classes thanks to this behavior:

trait Filtering {(1)    StringBuilder append(String str) {(2)        def subst = str.replace('o','')(3)        super.append(subst)(4)    }    String toString() { super.toString() }(5)}def sb = new StringBuilder().withTraits Filtering(6)sb.append('Groovy')assert sb.toString() == 'Grvy'(7)
1define a trait namedFiltering, supposed to be applied on aStringBuilder at runtime
2redefine theappend method
3remove all 'o’s from the string
4then delegate tosuper
5in casetoString is called, delegate tosuper.toString
6runtime implementation of theFiltering trait on aStringBuilder instance
7the string which has been appended no longer contains the lettero

In this example, whensuper.append is encountered, there is no other trait implemented by the target object, so themethod which is called is the originalappend method, that is to say the one fromStringBuilder. The same trickis used fortoString, so that the string representation of the proxy object which is generated delegates to thetoString of theStringBuilder instance.

Advanced features
SAM type coercion

If a trait defines a single abstract method, it is candidate for SAM (Single Abstract Method) type coercion. For example,imagine the following trait:

trait Greeter {    String greet() { "Hello $name" }(1)    abstract String getName()(2)}
1thegreet method is not abstract and calls the abstract methodgetName
2getName is an abstract method

SincegetName is thesingle abstract method in theGreeter trait, you can write:

Greeter greeter = { 'Alice' }(1)
1the closure "becomes" the implementation of thegetName single abstract method

or even:

void greet(Greeter g) { println g.greet() }(1)greet { 'Alice' }(2)
1the greet method accepts the SAM type Greeter as parameter
2we can call it directly with a closure
Differences with Java 8 default methods

In Java 8, interfaces can have default implementations of methods. If a class implements an interface and does not providean implementation for a default method, then the implementation from the interface is chosen. Traits behave the same butwith a major difference: the implementation from the trait isalways used if the class declares the trait in its interfacelistand that it doesn’t provide an implementationeven if a super class does.

This feature can be used to compose behaviors in a very precise way, in case you want to override the behavior of analready implemented method.

To illustrate the concept, let’s start with this simple example:

import groovy.test.GroovyTestCaseimport groovy.transform.CompileStaticimport org.codehaus.groovy.control.CompilerConfigurationimport org.codehaus.groovy.control.customizers.ASTTransformationCustomizerimport org.codehaus.groovy.control.customizers.ImportCustomizerclass SomeTest extends GroovyTestCase {    def config    def shell    void setup() {        config = new CompilerConfiguration()        shell = new GroovyShell(config)    }    void testSomething() {        assert shell.evaluate('1+1') == 2    }    void otherTest() { /* ... */ }}

In this example, we create a simple test case which uses two properties (config andshell) and uses those inmultiple test methods. Now imagine that you want to test the same, but with another distinct compiler configuration.One option is to create a subclass ofSomeTest:

class AnotherTest extends SomeTest {    void setup() {        config = new CompilerConfiguration()        config.addCompilationCustomizers( ... )        shell = new GroovyShell(config)    }}

It works, but what if you have actually multiple test classes, and that you want to test the new configuration for allthose test classes? Then you would have to create a distinct subclass for each test class:

class YetAnotherTest extends SomeTest {    void setup() {        config = new CompilerConfiguration()        config.addCompilationCustomizers( ... )        shell = new GroovyShell(config)    }}

Then what you see is that thesetup method of both tests is the same. The idea, then, is to create a trait:

trait MyTestSupport {    void setup() {        config = new CompilerConfiguration()        config.addCompilationCustomizers( new ASTTransformationCustomizer(CompileStatic) )        shell = new GroovyShell(config)    }}

Then use it in the subclasses:

class AnotherTest extends SomeTest implements MyTestSupport {}class YetAnotherTest extends SomeTest2 implements MyTestSupport {}...

It would allow us to dramatically reduce the boilerplate code, and reduces the risk of forgetting to change the setupcode in case we decide to change it. Even ifsetup is already implemented in the super class, since the test class declaresthe trait in its interface list, the behavior will be borrowed from the trait implementation!

This feature is in particular useful when you don’t have access to the super class source code. It can be used tomock methods or force a particular implementation of a method in a subclass. It lets you refactor your code to keepthe overridden logic in a single trait and inherit a new behavior just by implementing it. The alternative, of course,is to override the method inevery place you would have used the new code.

It’s worth noting that if you use runtime traits, the methods from the trait arealways preferred to those of the proxiedobject:
class Person {    String name(1)}trait Bob {    String getName() { 'Bob' }(2)}def p = new Person(name: 'Alice')assert p.name == 'Alice'(3)def p2 = p as Bob(4)assert p2.name == 'Bob'(5)
1thePerson class defines aname property which results in agetName method
2Bob is a trait which definesgetName as returningBob
3the default object will returnAlice
4p2 coercesp intoBob at runtime
5getName returnsBob becausegetName is taken from thetrait
Again, don’t forget that dynamic trait coercion returns a distinct object which only implements the originalinterfaces, as well as the traits.
Differences with mixins

There are several conceptual differences with mixins, as they are available in Groovy. Note that we are talking aboutruntime mixins, not the @Mixin annotation which is deprecated in favour of traits.

First of all, methods defined in a trait are visible in bytecode:

  • internally, the trait is represented as an interface (without default or static methods) and several helper classes

  • this means that an object implementing a trait effectively implements aninterface

  • those methods are visible from Java

  • they are compatible with type checking and static compilation

Methods added through a mixin are, on the contrary, only visible at runtime:

class A { String methodFromA() { 'A' } }(1)class B { String methodFromB() { 'B' } }(2)A.metaClass.mixin B(3)def o = new A()assert o.methodFromA() == 'A'(4)assert o.methodFromB() == 'B'(5)assert o instanceof A(6)assert !(o instanceof B)(7)
1classA definesmethodFromA
2classB definesmethodFromB
3mixin B into A
4we can callmethodFromA
5we can also callmethodFromB
6the object is an instance ofA
7but it’snot an instanceofB

The last point is actually a very important and illustrates a place where mixins have an advantage over traits: the instancesarenot modified, so if you mixin some class into another, there isn’t a third class generated, and methods which respond toA will continue responding to A even if mixed in.

Static methods, properties and fields
The following instructions are subject to caution. Static member support is work in progress and still experimental. Theinformation below is valid for 4.0.27 only.

It is possible to define static methods in a trait, but it comes with numerous limitations:

  • Traits with static methods cannot be compiled statically or type checked. All static methods,properties and field are accessed dynamically (it’s a limitation from the JVM).

  • Static methods do not appear within the generated interfaces for each trait.

  • The trait is interpreted as atemplate for the implementing class, which means that eachimplementing class will get its own static methods, properties and fields. So a static memberdeclared on a trait doesn’t belong to theTrait, but to its implementing class.

  • You should typically not mix static and instance methods of the same signature. The normalrules for applying traits apply (including multiple inheritance conflict resolution). If themethod chosen is static but some implemented trait has an instance variant, a compilation errorwill occur. If the method chosen is the instance variant, the static variant will be ignored(the behavior is similar to static methods in Java interfaces for this case).

Let’s start with a simple example:

trait TestHelper {    public static boolean CALLED = false(1)    static void init() {(2)        CALLED = true(3)    }}class Foo implements TestHelper {}Foo.init()(4)assert Foo.TestHelper__CALLED(5)
1the static field is declared in the trait
2a static method is also declared in the trait
3the static field is updatedwithin the trait
4a static methodinit is made available to the implementing class
5the static field isremapped to avoid the diamond issue

As usual, it is not recommended to use public fields. Anyway, should you want this, you must understand that the following code would fail:

Foo.CALLED = true

because there isno static fieldCALLED defined on the trait itself. Likewise, if you have two distinct implementing classes, each one gets a distinct static field:

class Bar implements TestHelper {}(1)class Baz implements TestHelper {}(2)Bar.init()(3)assert Bar.TestHelper__CALLED(4)assert !Baz.TestHelper__CALLED(5)
1classBar implements the trait
2classBaz also implements the trait
3init is only called onBar
4the static fieldCALLED onBar is updated
5but the static fieldCALLED onBaz is not, because it isdistinct
Inheritance of state gotchas

We have seen that traits are stateful. It is possible for a trait to define fields or properties, but when a class implements a trait, it gets those fields/properties ona per-trait basis. So consider the following example:

trait IntCouple {    int x = 1    int y = 2    int sum() { x+y }}

The trait defines two properties,x andy, as well as asum method. Now let’s create a class which implements the trait:

class BaseElem implements IntCouple {    int f() { sum() }}def base = new BaseElem()assert base.f() == 3

The result of callingf is3, becausef delegates tosum in the trait, which has state. But what if we write this instead?

class Elem implements IntCouple {    int x = 3(1)    int y = 4(2)    int f() { sum() }(3)}def elem = new Elem()
1Override propertyx
2Override propertyy
3Callsum from trait

If you callelem.f(), what is the expected output? Actually it is:

assert elem.f() == 3

The reason is that thesum method accesses thefields of the trait. So it is using thex andy values definedin the trait. If you want to use the values from the implementing class, then you need to dereference fields by usinggetters and setters, like in this last example:

trait IntCouple {    int x = 1    int y = 2    int sum() { getX()+getY() }}class Elem implements IntCouple {    int x = 3    int y = 4    int f() { sum() }}def elem = new Elem()assert elem.f() == 7
Self types
Type constraints on traits

Sometimes you will want to write a trait that can only be applied to some type. For example, you may want to apply atrait on a class that extends another class which is beyond your control, and still be able to call those methods.To illustrate this, let’s start with this example:

class CommunicationService {    static void sendMessage(String from, String to, String message) {(1)        println "$from sent [$message] to $to"    }}class Device { String id }(2)trait Communicating {    void sendMessage(Device to, String message) {        CommunicationService.sendMessage(id, to.id, message)(3)    }}class MyDevice extends Device implements Communicating {}(4)def bob = new MyDevice(id:'Bob')def alice = new MyDevice(id:'Alice')bob.sendMessage(alice,'secret')(5)
1AService class, beyond your control (in a library, …​) defines asendMessage method
2ADevice class, beyond your control (in a library, …​)
3Defines a communicating trait for devices that can call the service
4DefinesMyDevice as a communicating device
5The method from the trait is called, andid is resolved

It is clear, here, that theCommunicating trait can only apply toDevice. However, there’s no explicitcontract to indicate that, because traits cannot extend classes. However, the code compiles and runs perfectlyfine, becauseid in the trait method will be resolved dynamically. The problem is that there is nothing thatprevents the trait from being applied to any class which isnot aDevice. Any class which has anid wouldwork, while any class that does not have anid property would cause a runtime error.

The problem is even more complex if you want to enable type checking or apply@CompileStatic on the trait: becausethe trait knows nothing about itself being aDevice, the type checker will complain saying that it does not findtheid property.

One possibility is to explicitly add agetId method in the trait, but it would not solve all issues. What if a methodrequiresthis as a parameter, and actually requires it to be aDevice?

class SecurityService {    static void check(Device d) { if (d.id==null) throw new SecurityException() }}

If you want to be able to callthis in the trait, then you will explicitly need to castthis into aDevice. This canquickly become unreadable with explicit casts tothis everywhere.

The @SelfType annotation

In order to make this contract explicit, and to make the type checker aware of thetype of itself, Groovy providesa@SelfType annotation that will:

  • let you declare the types that a class that implements this trait must inherit or implement

  • throw a compile-time error if those type constraints are not satisfied

So in our previous example, we can fix the trait using the@groovy.transform.SelfType annotation:

@SelfType(Device)@CompileStatictrait Communicating {    void sendMessage(Device to, String message) {        SecurityService.check(this)        CommunicationService.sendMessage(id, to.id, message)    }}

Now if you try to implement this trait on a class that isnot a device, a compile-time error will occur:

class MyDevice implements Communicating {} // forgot to extend Device

The error will be:

class 'MyDevice' implements trait 'Communicating' but does not extend self type class 'Device'

In conclusion, self types are a powerful way of declaring constraints on traits without having to declare the contractdirectly in the trait or having to use casts everywhere, maintaining separation of concerns as tight as it should be.

Differences with Sealed annotation (incubating)

Both@Sealed and@SelfType restrict classes which use a trait but in orthogonal ways.Consider the following example:

interface HasHeight { double getHeight() }interface HasArea { double getArea() }@SelfType([HasHeight, HasArea])(1)@Sealed(permittedSubclasses=[UnitCylinder,UnitCube])(2)trait HasVolume {    double getVolume() { height * area }}final class UnitCube implements HasVolume, HasHeight, HasArea {    // for the purposes of this example: h=1, w=1, l=1    double height = 1d    double area = 1d}final class UnitCylinder implements HasVolume, HasHeight, HasArea {    // for the purposes of this example: h=1, diameter=1    // radius=diameter/2, area=PI * r^2    double height = 1d    double area = Math.PI * 0.5d**2}assert new UnitCube().volume == 1dassert new UnitCylinder().volume == 0.7853981633974483d
1All usages of theHasVolume trait must implement or extend bothHasHeight andHasArea
2OnlyUnitCube orUnitCylinder can use the trait

For the degenerate case where a single class implements a trait, e.g.:

final class Foo implements FooTrait {}

Then, either:

@SelfType(Foo)trait FooTrait {}

or:

@Sealed(permittedSubclasses='Foo')(1)trait FooTrait {}
1Or just@Sealed ifFoo andFooTrait are in the same source file

could express this constraint. Generally, the former of these is preferred.

Limitations
Compatibility with AST transformations
Traits are not officially compatible with AST transformations. Some of them, like@CompileStatic will be appliedon the trait itself (not on implementing classes), while others will apply on both the implementing class and the trait.There is absolutely no guarantee that an AST transformation will run on a trait as it does on a regular class, so use itat your own risk!
Prefix and postfix operations

Within traits, prefix and postfix operations are not allowed if they update a field of the trait:

trait Counting {    int x    void inc() {        x++(1)    }    void dec() {        --x(2)    }}class Counter implements Counting {}def c = new Counter()c.inc()
1x is defined within the trait, postfix increment is not allowed
2x is defined within the trait, prefix decrement is not allowed

A workaround is to use the+= operator instead.

1.4.6. Record classes (incubating)

Record classes, orrecords for short, are a special kind of classuseful for modelling plain data aggregates.They provide a compact syntax with less ceremony than normal classes.Groovy already has AST transforms such as@Immutable and@Canonicalwhich already dramatically reduce ceremony but records have beenintroduced in Java and record classes in Groovy are designed to alignwith Java record classes.

For example, suppose we want to create aMessage recordrepresenting an email message. For the purposes of this example,let’s simplify such a message to contain just afrom email address,ato email address, and a messagebody. We can define sucha record as follows:

record Message(String from, String to, String body) { }

We’d use the record class in the same way as a normal class, as shown below:

def msg = new Message('me@myhost.com', 'you@yourhost.net', 'Hello!')assert msg.toString() == 'Message[from=me@myhost.com, to=you@yourhost.net, body=Hello!]'

The reduced ceremony saves us from defining explicit fields, getters andtoString,equals andhashCode methods. In fact, it’s a shorthandfor the following rough equivalent:

final class Message extends Record {    private final String from    private final String to    private final String body    private static final long serialVersionUID = 0    /* constructor(s) */    final String toString() { /*...*/ }    final boolean equals(Object other) { /*...*/ }    final int hashCode() { /*...*/ }    String from() { from }    // other getters ...}

Note the special naming convention for record getters. They are the same name as the field(rather than the often common JavaBean convention of capitalized with a "get" prefix).Rather than referring to a record’s fields or properties, the termcomponentis typically used for records. So ourMessage record hasfrom,to, andbody components.

Like in Java, you can override the normally implicitly supplied methodsby writing your own:

record Point3D(int x, int y, int z) {    String toString() {        "Point3D[coords=$x,$y,$z]"    }}assert new Point3D(10, 20, 30).toString() == 'Point3D[coords=10,20,30]'

You can also use generics with records in the normal way. For example, consider the followingCoord record definition:

record Coord<T extends Number>(T v1, T v2){    double distFromOrigin() { Math.sqrt(v1()**2 + v2()**2 as double) }}

It can be used as follows:

def r1 = new Coord<Integer>(3, 4)assert r1.distFromOrigin() == 5def r2 = new Coord<Double>(6d, 2.5d)assert r2.distFromOrigin() == 6.5d
Special record features
Compact constructor

Records have an implicit constructor. This can be overridden in the normal wayby providing your own constructor - you need to make sure you set all the fieldsif you do this.However, for succinctness, a compact constructor syntax can be used wherethe parameter declaration part of a normal constructor is elided.For this special case, the normal implicit constructor is still providedbut is augmented by the supplied statements in the compact constructor definition:

public record Warning(String message) {    public Warning {        Objects.requireNonNull(message)        message = message.toUpperCase()    }}def w = new Warning('Help')assert w.message() == 'HELP'
Serializability

Groovynative records follow thespecial conventionsfor serializability which apply to Java records.Groovyrecord-like classes (discussed below) follow normal Java class serializability conventions.

Groovy enhancements
Argument defaults

Groovy supports default values for constructor arguments.This capability is also available for records as shown in the following record definitionwhich has default values fory andcolor:

record ColoredPoint(int x, int y = 0, String color = 'white') {}

Arguments when left off (dropping one or more arguments from the right) are replacedwith their defaults values as shown in the following example:

assert new ColoredPoint(5, 5, 'black').toString() == 'ColoredPoint[x=5, y=5, color=black]'assert new ColoredPoint(5, 5).toString() == 'ColoredPoint[x=5, y=5, color=white]'assert new ColoredPoint(5).toString() == 'ColoredPoint[x=5, y=0, color=white]'

This processing follows normal Groovy conventions for default arguments for constructors, essentially automatically providing the constructors with the following signatures:

ColoredPoint(int, int, String)ColoredPoint(int, int)ColoredPoint(int)

Named arguments may also be used (default values also apply here):

assert new ColoredPoint(x: 5).toString() == 'ColoredPoint[x=5, y=0, color=white]'assert new ColoredPoint(x: 0, y: 5).toString() == 'ColoredPoint[x=0, y=5, color=white]'

You can disable default argument processing as shown here:

@TupleConstructor(defaultsMode=DefaultsMode.OFF)record ColoredPoint2(int x, int y, String color) {}assert new ColoredPoint2(4, 5, 'red').toString() == 'ColoredPoint2[x=4, y=5, color=red]'

This will produce a single constructor as per the default with Java.It will be an error if you drop off arguments in this scenario.

You can force all properties to have a default value as shown here:

@TupleConstructor(defaultsMode=DefaultsMode.ON)record ColoredPoint3(int x, int y = 0, String color = 'white') {}assert new ColoredPoint3(y: 5).toString() == 'ColoredPoint3[x=0, y=5, color=white]'

Any property/field without an explicit initial value will be given the default value for the argument’s type (null, or zero/false for primitives).

Diving deeper

We previously described aMessage record and displayed it’s rough equivalent.Groovy in fact steps through an intermediate stage where therecord keywordis replaced by theclass keyword and an accompanying@RecordType annotation:

@RecordTypeclass Message {    String from    String to    String body}

Then@RecordType itself is processed as ameta-annotation (annotation collector)and expanded into its constituent sub-annotations such as@TupleConstructor,@POJO,@RecordBase, and others. This is in some sense an implementation detail which can often be ignored.However, if you wish to customise or configure the record implementation,you may wish to drop back to the@RecordType style or augment your record classwith one of the constituent sub-annotations.

DeclarativetoString customization

As per Java, you can customize a record’stoString method by writing your own.If you prefer a more declarative style, you can alternatively use Groovy’s@ToString transformto override the default recordtoString.As an example, you can a three-dimensional point record as follows:

package threedimport groovy.transform.ToString@ToString(ignoreNulls=true, cache=true, includeNames=true,          leftDelimiter='[', rightDelimiter=']', nameValueSeparator='=')record Point(Integer x, Integer y, Integer z=null) { }assert new Point(10, 20).toString() == 'threed.Point[x=10, y=20]'

We customise thetoString by including the package name (excluded by default for records)and by caching thetoString value since it won’t change for this immutable record.We are also ignoring null values (the default value forz in our definition).

We can have a similar definition for a two-dimensional point:

package twodimport groovy.transform.ToString@ToString(ignoreNulls=true, cache=true, includeNames=true,          leftDelimiter='[', rightDelimiter=']', nameValueSeparator='=')record Point(Integer x, Integer y) { }assert new Point(10, 20).toString() == 'twod.Point[x=10, y=20]'

We can see here that without the package name it would have the same toString as our previous example.

Obtaining a list of the record component values

We can obtain the component values from a record as a list like so:

record Point(int x, int y, String color) { }def p = new Point(100, 200, 'green')def (x, y, c) = p.toList()assert x == 100assert y == 200assert c == 'green'

You can use@RecordOptions(toList=false) to disable this feature.

Obtaining a map of the record component values

We can obtain the component values from a record as a map like so:

record Point(int x, int y, String color) { }def p = new Point(100, 200, 'green')assert p.toMap() == [x: 100, y: 200, color: 'green']

You can use@RecordOptions(toMap=false) to disable this feature.

Obtaining the number of components in a record

We can obtain the number of components in a record like so:

record Point(int x, int y, String color) { }def p = new Point(100, 200, 'green')assert p.size() == 3

You can use@RecordOptions(size=false) to disable this feature.

Obtaining the nth component from a record

We can use Groovy’s normal positional indexing to obtain a particular component in a record like so:

record Point(int x, int y, String color) { }def p = new Point(100, 200, 'green')assert p[1] == 200

You can use@RecordOptions(getAt=false) to disable this feature.

Optional Groovy features
Copying

It can be useful to make a copy of a record with some components changed.This can be done using an optionalcopyWith method which takes named arguments.Record components are set from the supplied arguments.For components not mentioned, a (shallow) copy of the original record component is used.Here is how you might usecopyWith for theFruit record:

@RecordOptions(copyWith=true)record Fruit(String name, double price) {}def apple = new Fruit('Apple', 11.6)assert 'Apple' == apple.name()assert 11.6 == apple.price()def orange = apple.copyWith(name: 'Orange')assert orange.toString() == 'Fruit[name=Orange, price=11.6]'

ThecopyWith functionality can be disabled by setting theRecordOptions#copyWith annotation attribute tofalse.

Deep immutability

As with Java, records by default offer shallow immutability.Groovy’s@Immutable transform performs defensive copying for a range of mutabledata types. Records can make use of this defensive copying to gain deep immutability as follows:

@ImmutablePropertiesrecord Shopping(List items) {}def items = ['bread', 'milk']def shop = new Shopping(items)items << 'chocolate'assert shop.items() == ['bread', 'milk']

These examples illustrate the principal behindGroovy’s record feature offering three levels of convenience:

  • Using therecord keyword for maximum succinctness

  • Supporting low-ceremony customization using declarative annotations

  • Allowing normal method implementations when full control is required

Obtaining the components of a record as a typed tuple

You can obtain the components of a record as a typed tuple:

import groovy.transform.*@RecordOptions(components=true)record Point(int x, int y, String color) { }@CompileStaticdef method() {    def p1 = new Point(100, 200, 'green')    def (int x1, int y1, String c1) = p1.components()    assert x1 == 100    assert y1 == 200    assert c1 == 'green'    def p2 = new Point(10, 20, 'blue')    def (x2, y2, c2) = p2.components()    assert x2 * 10 == 100    assert y2 ** 2 == 400    assert c2.toUpperCase() == 'BLUE'    def p3 = new Point(1, 2, 'red')    assert p3.components() instanceof Tuple3}method()

Groovy has a limited number ofTupleN classes.If you have a large number of components in your record, you might not be able to use this feature.

Other differences to Java

Groovy supports creatingrecord-like classes as well as native records.Record-like classes don’t extend Java’sRecord class and such classeswon’t be seen by Java as records but will otherwise have similar properties.

The@RecordOptions annotation (part of@RecordType) supports amode annotation attributewhich can take one of three values (withAUTO being the default):

NATIVE

Produces a class similar to what Java would do. Produces an error when compiling on JDKs earlier than JDK16.

EMULATE

Produces a record-like class for all JDK versions.

AUTO

Produces a native record for JDK16+ and emulates the record otherwise.

Whether you use therecord keyword or the@RecordType annotationis independent of the mode.

1.4.7. Sealed hierarchies (incubating)

Sealed classes, interfaces and traits restrict which subclasses can extend/implement them.Prior to sealed classes, class hierarchy designers had two main options:

  • Make a class final to allow no extension.

  • Make the class public and non-final to allow extension by anyone.

Sealed classes provide a middle-ground compared to these all or nothing choices.

Sealed classes are also more flexible than other tricks previously usedto try to achieve a middle-ground. For example, for class hierarchies,access modifiers like protected and package-private give some ability to restrict inheritancehierarchies but often at the expense of flexible use of those hierarchies.

Sealed hierarchies provide full inheritance within a known hierarchy of classes, interfacesand traits but disable or only provide controlled inheritance outside the hierarchy.

As an example, suppose we want to create a shape hierarchy containingonly circles and squares. We also want a shape interface tobe able to refer to instances in our hierarchy.We can create the hierarchy as follows:

sealed interface ShapeI permits Circle,Square { }final class Circle implements ShapeI { }final class Square implements ShapeI { }

Groovy also supports an alternative annotation syntax.We think the keyword style is nicer but you might choose the annotation style if your editor doesn’t yet have Groovy 4 support.

@Sealed(permittedSubclasses=[Circle,Square]) interface ShapeI { }final class Circle implements ShapeI { }final class Square implements ShapeI { }

We can have a reference of typeShapeI which, thanks to thepermits clause,can point to either aCircle orSquare and, since our classes arefinal,we know no additional classes will be added to our hierarchy in the future.At least not without changing thepermits clause and recompiling.

In general, we might want to have some parts of our class hierarchyimmediately locked down like we have here, where we marked thesubclasses asfinal but other times we might want to allow furthercontrolled inheritance.

sealed class Shape permits Circle,Polygon,Rectangle { }final class Circle extends Shape { }class Polygon extends Shape { }non-sealed class RegularPolygon extends Polygon { }final class Hexagon extends Polygon { }sealed class Rectangle extends Shape permits Square{ }final class Square extends Rectangle { }
<Click to see the alternate annotations syntax>
@Sealed(permittedSubclasses=[Circle,Polygon,Rectangle]) class Shape { }final class Circle extends Shape { }class Polygon extends Shape { }@NonSealed class RegularPolygon extends Polygon { }final class Hexagon extends Polygon { }@Sealed(permittedSubclasses=Square) class Rectangle extends Shape { }final class Square extends Rectangle { }

 
In this example, our permitted subclasses forShape areCircle,Polygon, andRectangle.Circle isfinal and hence that part of the hierarchy cannot be extended.Polygon is implicitly non-sealed andRegularPolygon is explicitly marked asnon-sealed.That means our hierarchy is open to any further extension by subclassing,as seen withPolygon → RegularPolygon andRegularPolygon → Hexagon.Rectangle is itself sealed which means that part of the hierarchy can be extendedbut only in a controlled way (onlySquare is permitted).

Sealed classes are useful for creating enum-like related classeswhich need to contain instance specific data. For instance, we might have the following enum:

enum Weather { Rainy, Cloudy, Sunny }def forecast = [Weather.Rainy, Weather.Sunny, Weather.Cloudy]assert forecast.toString() == '[Rainy, Sunny, Cloudy]'

but we now wish to also add weather specific instance data to weather forecasts.We can alter our abstraction as follows:

sealed abstract class Weather { }@Immutable(includeNames=true) class Rainy extends Weather { Integer expectedRainfall }@Immutable(includeNames=true) class Sunny extends Weather { Integer expectedTemp }@Immutable(includeNames=true) class Cloudy extends Weather { Integer expectedUV }def forecast = [new Rainy(12), new Sunny(35), new Cloudy(6)]assert forecast.toString() == '[Rainy(expectedRainfall:12), Sunny(expectedTemp:35), Cloudy(expectedUV:6)]'

Sealed hierarchies are also useful when specifying Algebraic or Abstract Data Types (ADTs) as shown in the following example:

import groovy.transform.*sealed interface Tree<T> {}@Singleton final class Empty implements Tree {    String toString() { 'Empty' }}@Canonical final class Node<T> implements Tree<T> {    T value    Tree<T> left, right}Tree<Integer> tree = new Node<>(42, new Node<>(0, Empty.instance, Empty.instance), Empty.instance)assert tree.toString() == 'Node(42, Node(0, Empty, Empty), Empty)'

Sealed hierarchies work well with records as shown in the following example:

sealed interface Expr {}record ConstExpr(int i) implements Expr {}record PlusExpr(Expr e1, Expr e2) implements Expr {}record MinusExpr(Expr e1, Expr e2) implements Expr {}record NegExpr(Expr e) implements Expr {}def threePlusNegOne = new PlusExpr(new ConstExpr(3), new NegExpr(new ConstExpr(1)))assert threePlusNegOne.toString() == 'PlusExpr[e1=ConstExpr[i=3], e2=NegExpr[e=ConstExpr[i=1]]]'
Differences to Java
  • Java provides no default modifier for subclasses of sealed classesand requires that one offinal,sealed ornon-sealed be specified.Groovy defaults tonon-sealed but you can still usenon-sealed/@NonSealed if you wish.We anticipate the style checking tool CodeNarc will eventually have a rule thatlooks for the presence ofnon-sealed so developers wanting that stricterstyle will be able to use CodeNarc and that rule if they want.

  • Currently, Groovy doesn’t check that all classes mentioned inpermittedSubclassesare available at compile-time and compiled along with the base sealed class.This may change in a future version of Groovy.

Groovy supports annotating classes as sealed as well as "native" sealed classes.

The@SealedOptions annotation supports amode annotation attributewhich can take one of three values (withAUTO being the default):

NATIVE

Produces a class similar to what Java would do.Produces an error when compiling on JDKs earlier than JDK17.

EMULATE

Indicates the class is sealed using the@Sealed annotation.This mechanism works with the Groovy compiler for JDK8+ but is not recognised by the Java compiler.

AUTO

Produces a native record for JDK17+ and emulates the record otherwise.

Whether you use thesealed keyword or the@Sealed annotationis independent of the mode.

1.5. Closures

This chapter covers Groovy Closures. A closure in Groovy is an open, anonymous, block of code that can take arguments,return a value and be assigned to a variable. A closure may reference variables declared in its surrounding scope. Inopposition to the formal definition of a closure,Closure in the Groovy language can also contain free variables whichare defined outside of its surrounding scope. While breaking the formal concept of a closure, it offers a variety ofadvantages which are described in this chapter.

1.5.1. Syntax

Defining a closure

A closure definition follows this syntax:

{ [closureParameters -> ] statements }

Where[closureParameters->] is an optional comma-delimited list ofparameters, and statements are 0 or more Groovy statements. The parameterslook similar to a method parameter list, and these parameters may betyped or untyped.

When a parameter list is specified, the-> characteris required and serves to separate the arguments from the closure body.Thestatements portion consists of 0, 1, or many Groovy statements.

Some examples of valid closure definitions:

{ item++ }(1){ -> item++ }(2){ println it }(3){ it -> println it }(4){ name -> println name }(5){ String x, int y ->(6)    println "hey ${x} the value is ${y}"}{ reader ->(7)    def line = reader.readLine()    line.trim()}
1A closure referencing a variable nameditem
2It is possible to explicitly separate closure parameters from code by adding an arrow (->)
3A closure using an implicit parameter (it)
4An alternative version whereit is an explicit parameter
5In that case it is often better to use an explicit name for the parameter
6A closure accepting two typed parameters
7A closure can contain multiple statements
Closures as an object

A closure is an instance of thegroovy.lang.Closure class, making it assignable to a variable or a field as anyother variable, despite being a block of code:

def listener = { e -> println "Clicked on $e.source" }(1)assert listener instanceof ClosureClosure callback = { println 'Done!' }(2)Closure<Boolean> isTextFile = {    File it -> it.name.endsWith('.txt')(3)}
1You can assign a closure to a variable, and it is an instance ofgroovy.lang.Closure
2If not usingdef orvar, usegroovy.lang.Closure as the type
3Optionally, you can specify the return type of the closure by using the generic type ofgroovy.lang.Closure
Calling a closure

A closure, as an anonymous block of code, can be called like any other method. If you define a closure which takesno argument like this:

def code = { 123 }

Then the code inside the closure will only be executed when youcall the closure, which can be done by using thevariable as if it was a regular method:

assert code() == 123

Alternatively, you can be explicit and use thecall method:

assert code.call() == 123

The principle is the same if the closure accepts arguments:

def isOdd = { int i -> i%2 != 0 }(1)assert isOdd(3) == true(2)assert isOdd.call(2) == false(3)def isEven = { it%2 == 0 }(4)assert isEven(3) == false(5)assert isEven.call(2) == true(6)
1define a closure which accepts anint as a parameter
2it can be called directly
3or using thecall method
4same goes for a closure with an implicit argument (it)
5which can be called directly using(arg)
6or usingcall

Unlike a method, a closurealways returns a value when called. The next section discusses how to declare closure arguments, when to use them and what is theimplicit"it" parameter.

1.5.2. Parameters

Normal parameters

Parameters of closures follow the same principle as parameters of regular methods:

  • an optional type

  • a name

  • an optional default value

Parameters are separated with commas:

def closureWithOneArg = { str -> str.toUpperCase() }assert closureWithOneArg('groovy') == 'GROOVY'def closureWithOneArgAndExplicitType = { String str -> str.toUpperCase() }assert closureWithOneArgAndExplicitType('groovy') == 'GROOVY'def closureWithTwoArgs = { a,b -> a+b }assert closureWithTwoArgs(1,2) == 3def closureWithTwoArgsAndExplicitTypes = { int a, int b -> a+b }assert closureWithTwoArgsAndExplicitTypes(1,2) == 3def closureWithTwoArgsAndOptionalTypes = { a, int b -> a+b }assert closureWithTwoArgsAndOptionalTypes(1,2) == 3def closureWithTwoArgAndDefaultValue = { int a, int b=2 -> a+b }assert closureWithTwoArgAndDefaultValue(1) == 3
Implicit parameter

When a closure does not explicitly define a parameter list (using->), a closurealways defines an implicitparameter, namedit. This means that this code:

def greeting = { "Hello, $it!" }assert greeting('Patrick') == 'Hello, Patrick!'

is strictly equivalent to this one:

def greeting = { it -> "Hello, $it!" }assert greeting('Patrick') == 'Hello, Patrick!'

If you want to declare a closure which accepts no argument and must be restricted to calls without arguments,then youmust declare it with an explicit empty argument list:

def magicNumber = { -> 42 }// this call will fail because the closure doesn't accept any argumentmagicNumber(11)
Varargs

It is possible for a closure to declare variable arguments like any other method.Vargs methods are methods thatcan accept a variable number of arguments if the last parameter is of variable length (or an array) like in the nextexamples:

def concat1 = { String... args -> args.join('') }(1)assert concat1('abc','def') == 'abcdef'(2)def concat2 = { String[] args -> args.join('') }(3)assert concat2('abc', 'def') == 'abcdef'def multiConcat = { int n, String... args ->(4)    args.join('')*n}assert multiConcat(2, 'abc','def') == 'abcdefabcdef'
1A closure accepting a variable number of strings as first parameter
2It may be called using any number of argumentswithout having to explicitly wrap them into an array
3The same behavior is directly available if theargs parameter is declared as an array
4As long as thelast parameter is an array or an explicit vargs type

1.5.3. Delegation strategy

Groovy closures vs lambda expressions

Groovy defines closures asinstances of the Closure class. It makes it very different fromlambda expressions in Java 8. Delegation is akey concept in Groovy closures which has no equivalent in lambdas. The ability tochange the delegate orchange thedelegation strategy of closures make it possible to design beautiful domain specific languages (DSLs) in Groovy.

Owner, delegate and this

To understand the concept of delegate, we must first explain the meaning ofthis inside a closure. A closure actuallydefines 3 distinct things:

  • this corresponds to theenclosing class where the closure is defined

  • owner corresponds to theenclosing object where the closure is defined, which may be either a class or a closure

  • delegate corresponds to a third party object where methods calls or properties are resolved whenever the receiver ofthe message is not defined

The meaning of this

In a closure, callinggetThisObject will return the enclosing class where the closure is defined. It is equivalent tousing an explicitthis:

class Enclosing {    void run() {        def whatIsThisObject = { getThisObject() }(1)        assert whatIsThisObject() == this(2)        def whatIsThis = { this }(3)        assert whatIsThis() == this(4)    }}class EnclosedInInnerClass {    class Inner {        Closure cl = { this }(5)    }    void run() {        def inner = new Inner()        assert inner.cl() == inner(6)    }}class NestedClosures {    void run() {        def nestedClosures = {            def cl = { this }(7)            cl()        }        assert nestedClosures() == this(8)    }}
1a closure is defined inside theEnclosing class, and returnsgetThisObject
2calling the closure will return the instance ofEnclosing where the closure is defined
3in general, you will just want to use the shortcutthis notation
4and it returnsexactly the same object
5if the closure is defined in an inner class
6this in the closurewill return the inner class, not the top-level one
7in case of nested closures, like herecl being defined inside the scope ofnestedClosures
8thenthis corresponds to the closest outer class, not the enclosing closure!

It is of course possible to call methods from the enclosing class this way:

class Person {    String name    int age    String toString() { "$name is $age years old" }    String dump() {        def cl = {            String msg = this.toString()(1)            println msg            msg        }        cl()    }}def p = new Person(name:'Janice', age:74)assert p.dump() == 'Janice is 74 years old'
1the closure callstoString onthis, which will actually call thetoString method on the enclosing object,that is to say thePerson instance
Owner of a closure

The owner of a closure is very similar to the definition ofthis in a closure with a subtle difference:it will return the direct enclosing object, be it a closure or a class:

class Enclosing {    void run() {        def whatIsOwnerMethod = { getOwner() }(1)        assert whatIsOwnerMethod() == this(2)        def whatIsOwner = { owner }(3)        assert whatIsOwner() == this(4)    }}class EnclosedInInnerClass {    class Inner {        Closure cl = { owner }(5)    }    void run() {        def inner = new Inner()        assert inner.cl() == inner(6)    }}class NestedClosures {    void run() {        def nestedClosures = {            def cl = { owner }(7)            cl()        }        assert nestedClosures() == nestedClosures(8)    }}
1a closure is defined inside theEnclosing class, and returnsgetOwner
2calling the closure will return the instance ofEnclosing where the closure is defined
3in general, you will just want to use the shortcutowner notation
4and it returnsexactly the same object
5if the closure is defined in an inner class
6owner in the closurewill return the inner class, not the top-level one
7but in case of nested closures, like herecl being defined inside the scope ofnestedClosures
8thenowner corresponds to the enclosing closure, hence a different object fromthis!
Delegate of a closure

The delegate of a closure can be accessed by using thedelegate property or calling thegetDelegate method. It is apowerful concept for building domain specific languages in Groovy. Whilethis andownerrefer to the lexical scope of a closure, the delegate is a user defined object that a closure will use. By default, thedelegate is set toowner:

class Enclosing {    void run() {        def cl = { getDelegate() }(1)        def cl2 = { delegate }(2)        assert cl() == cl2()(3)        assert cl() == this(4)        def enclosed = {            { -> delegate }.call()(5)        }        assert enclosed() == enclosed(6)    }}
1you can get the delegate of a closure calling thegetDelegate method
2or using thedelegate property
3both return the same object
4which is the enclosing class or closure
5in particular in case of nested closures
6delegate will correspond to theowner

The delegate of a closure can be changed toany object. Let’s illustrate this by creating two classes which are notsubclasses of each other but both define a property calledname:

class Person {    String name}class Thing {    String name}def p = new Person(name: 'Norman')def t = new Thing(name: 'Teapot')

Then let’s define a closure which fetches thename property on the delegate:

def upperCasedName = { delegate.name.toUpperCase() }

Then by changing the delegate of the closure, you can see that the target object will change:

upperCasedName.delegate = passert upperCasedName() == 'NORMAN'upperCasedName.delegate = tassert upperCasedName() == 'TEAPOT'

At this point, the behavior is not different from having atarget variable defined in the lexical scope of the closure:

def target = pdef upperCasedNameUsingVar = { target.name.toUpperCase() }assert upperCasedNameUsingVar() == 'NORMAN'

However, there are major differences:

  • in the last example,target is a local variable referenced from within the closure

  • the delegate can be used transparently, that is to say without prefixing method calls withdelegate. as explainedin the next paragraph.

Delegation strategy

Whenever, in a closure, a property is accessed without explicitly setting a receiver object, then a delegation strategyis involved:

class Person {    String name}def p = new Person(name:'Igor')def cl = { name.toUpperCase() }(1)cl.delegate = p(2)assert cl() == 'IGOR'(3)
1name is not referencing a variable in the lexical scope of the closure
2we can change the delegate of the closure to be an instance ofPerson
3and the method call will succeed

The reason this code works is that thename property will be resolved transparently on thedelegate object! This isa very powerful way to resolve properties or method calls inside closures. There’s no need to set an explicitdelegate.receiver: the call will be made because the default delegation strategy of the closure makes it so. A closure actuallydefines multiple resolution strategies that you can choose:

  • Closure.OWNER_FIRST is thedefault strategy. If a property/method exists on theowner, then it will be called onthe owner. If not, then thedelegate is used.

  • Closure.DELEGATE_FIRST reverses the logic: thedelegate is used first, then theowner

  • Closure.OWNER_ONLY will only resolve the property/method lookup on the owner: the delegate will be ignored.

  • Closure.DELEGATE_ONLY will only resolve the property/method lookup on the delegate: the owner will be ignored.

  • Closure.TO_SELF can be used by developers who need advanced meta-programming techniques and wish to implement acustom resolution strategy: the resolution will not be made on the owner or the delegate but only on the closure classitself. It makes only sense to use this if you implement your own subclass ofClosure.

Let’s illustrate the default "owner first" strategy with this code:

class Person {    String name    def pretty = { "My name is $name" }(1)    String toString() {        pretty()    }}class Thing {    String name(2)}def p = new Person(name: 'Sarah')def t = new Thing(name: 'Teapot')assert p.toString() == 'My name is Sarah'(3)p.pretty.delegate = t(4)assert p.toString() == 'My name is Sarah'(5)
1for the illustration, we define a closure member which references "name"
2both thePerson and theThing class define aname property
3Using the default strategy, thename property is resolved on the owner first
4so if we change thedelegate tot which is an instance ofThing
5there is no change in the result:name is first resolved on theowner of the closure

However, it is possible to change the resolution strategy of the closure:

p.pretty.resolveStrategy = Closure.DELEGATE_FIRSTassert p.toString() == 'My name is Teapot'

By changing theresolveStrategy, we are modifying the way Groovy will resolve the "implicit this" references: in thiscase,name will first be looked in the delegate, then if not found, on the owner. Sincename is defined in thedelegate, an instance ofThing, then this value is used.

The difference between "delegate first" and "delegate only" or "owner first" and "owner only" can be illustrated if oneof the delegate (resp. owner) doesnot have such a method or property:

class Person {    String name    int age    def fetchAge = { age }}class Thing {    String name}def p = new Person(name:'Jessica', age:42)def t = new Thing(name:'Printer')def cl = p.fetchAgecl.delegate = passert cl() == 42(1)cl.delegate = tassert cl() == 42(1)cl.resolveStrategy = Closure.DELEGATE_ONLYcl.delegate = passert cl() == 42(2)cl.delegate = ttry {    cl()(3)    assert false} catch (MissingPropertyException ex) {    // "age" is not defined on the delegate}
1for "owner first" it doesn’t matter what the delegate is
2for "delegate only" havingp as the delegate succeeds
3for "delegate only" havingt as the delegate fails

In this example, we define two classes which both have aname property but only thePerson class declares anage.ThePerson class also declares a closure which referencesage. We can change the default resolution strategy from"owner first" to "delegate only". Since the owner of the closure is thePerson class, then we can check that if thedelegate is an instance ofPerson, calling the closure is successful, but if we call it with a delegate being aninstance ofThing, it fails with agroovy.lang.MissingPropertyException. Despite the closure being defined insidethePerson class, the owner is not used.

A comprehensive explanation about how to use this feature to develop DSLs can be found in adedicated section of the manual.
Delegation strategy in the presence of metaprogramming

When describing the "owner first" delegation strategy we spokeabout using a property/method from the owner if it "existed" otherwiseusing the respective property/method from the delegate. And a similarstory for "delegate first" but in reverse. Instead of using the word"existed", it would have been more accurate to use the wording "handled".That means that for "owner first", if the property/method exists inthe owner, or it has a propertyMissing/methodMissing hook, then the ownerwill handle the member access.

We can see this in action with a slightly altered version of our previous example:

class Person {    String name    int age    def fetchAge = { age }}class Thing {    String name    def propertyMissing(String name) { -1 }}def p = new Person(name:'Jessica', age:42)def t = new Thing(name:'Printer')def cl = p.fetchAgecl.resolveStrategy = Closure.DELEGATE_FIRSTcl.delegate = passert cl() == 42cl.delegate = tassert cl() == -1

In this example, even though our instance of theThing class (our delegate for the last use ofcl) has noage property,the fact that it handles the missing property via itspropertyMissing hook,means thatage will be-1.

1.5.4. Closures in GStrings

Take the following code:

def x = 1def gs = "x = ${x}"assert gs == 'x = 1'

The code behaves as you would expect, but what happens if you add:

x = 2assert gs == 'x = 2'

You will see that the assert fails! There are two reasons for this:

  • a GString only evaluates lazily thetoString representation of values

  • the syntax${x} in a GString doesnot represent a closure but anexpression to$x, evaluated when the GStringis created.

In our example, theGString is created with an expression referencingx. When theGString is created, thevalueofx is 1, so theGString is created with a value of 1. When the assert is triggered, theGString is evaluatedand 1 is converted to aString usingtoString. When we changex to 2, we did change the value ofx, but it isa different object, and theGString still references the old one.

AGString will only change itstoString representation if the values it references are mutating. If the referenceschange, nothing will happen.

If you need a real closure in a GString and for example enforce lazy evaluation of variables, you need to use thealternate syntax${→ x} like in the fixed example:

def x = 1def gs = "x = ${-> x}"assert gs == 'x = 1'x = 2assert gs == 'x = 2'

And let’s illustrate how it differs from mutation with this code:

class Person {    String name    String toString() { name }(1)}def sam = new Person(name:'Sam')(2)def lucy = new Person(name:'Lucy')(3)def p = sam(4)def gs = "Name: ${p}"(5)assert gs == 'Name: Sam'(6)p = lucy(7)assert gs == 'Name: Sam'(8)sam.name = 'Lucy'(9)assert gs == 'Name: Lucy'(10)
1thePerson class has atoString method returning thename property
2we create a firstPerson namedSam
3we create anotherPerson namedLucy
4thep variable is set toSam
5and a closure is created, referencing the value ofp, that is to saySam
6so when we evaluate the string, it returnsSam
7if we changep toLucy
8the string still evaluates toSam because it was thevalue ofp when theGString was created
9so if we mutateSam to change the name toLucy
10this time theGString is correctly mutated

So if you don’t want to rely on mutating objects or wrapping objects, youmust use closures inGString by explicitlydeclaring an empty argument list:

class Person {    String name    String toString() { name }}def sam = new Person(name:'Sam')def lucy = new Person(name:'Lucy')def p = sam// Create a GString with lazy evaluation of "p"def gs = "Name: ${-> p}"assert gs == 'Name: Sam'p = lucyassert gs == 'Name: Lucy'

1.5.5. Closure coercion

Closures can be converted into interfaces or single-abstract method types. Please refer tothis section of the manual for a complete description.

1.5.6. Functional programming

Closures, likelambda expressions in Java 8 are at the core of the functional programming paradigm in Groovy. Some functional programmingoperations on functions are available directly on theClosure class, like illustrated in this section.

Currying

In Groovy, currying refers to the concept of partial application. It doesnot correspond to the real concept of curryingin functional programming because of the different scoping rules that Groovy applies on closures. Currying in Groovy willlet you set the value of one parameter of a closure, and it will return a new closure accepting one less argument.

Left currying

Left currying is the fact of setting the left-most parameter of a closure, like in this example:

def nCopies = { int n, String str -> str*n }(1)def twice = nCopies.curry(2)(2)assert twice('bla') == 'blabla'(3)assert twice('bla') == nCopies(2, 'bla')(4)
1thenCopies closure defines two parameters
2curry will set the first parameter to2, creating a new closure (function) which accepts a singleString
3so the new function call be called with only aString
4and it is equivalent to callingnCopies with two parameters
Right currying

Similarly to left currying, it is possible to set the right-most parameter of a closure:

def nCopies = { int n, String str -> str*n }(1)def blah = nCopies.rcurry('bla')(2)assert blah(2) == 'blabla'(3)assert blah(2) == nCopies(2, 'bla')(4)
1thenCopies closure defines two parameters
2rcurry will set the last parameter tobla, creating a new closure (function) which accepts a singleint
3so the new function call be called with only anint
4and it is equivalent to callingnCopies with two parameters
Index based currying

In case a closure accepts more than 2 parameters, it is possible to set an arbitrary parameter usingncurry:

def volume = { double l, double w, double h -> l*w*h }(1)def fixedWidthVolume = volume.ncurry(1, 2d)(2)assert volume(3d, 2d, 4d) == fixedWidthVolume(3d, 4d)(3)def fixedWidthAndHeight = volume.ncurry(1, 2d, 4d)(4)assert volume(3d, 2d, 4d) == fixedWidthAndHeight(3d)(5)
1thevolume function defines 3 parameters
2ncurry will set the second parameter (index = 1) to2d, creating a new volume function which accepts length and height
3that function is equivalent to callingvolume omitting the width
4it is also possible to set multiple parameters, starting from the specified index
5the resulting function accepts as many parameters as the initial one minus the number of parameters set byncurry
Memoization

Memoization allows the result of the call of a closure to be cached. It is interesting if the computation done by afunction (closure) is slow, but you know that this function is going to be called often with the same arguments. Atypical example is the Fibonacci suite. A naive implementation may look like this:

def fibfib = { long n -> n<2?n:fib(n-1)+fib(n-2) }assert fib(15) == 610 // slow!

It is a naive implementation because 'fib' is often called recursively with the same arguments, leading to an exponentialalgorithm:

  • computingfib(15) requires the result offib(14) andfib(13)

  • computingfib(14) requires the result offib(13) andfib(12)

Since calls are recursive, you can already see that we will compute the same values again and again, although they couldbe cached. This naive implementation can be "fixed" by caching the result of calls usingmemoize:

fib = { long n -> n<2?n:fib(n-1)+fib(n-2) }.memoize()assert fib(25) == 75025 // fast!
The cache worksusing the actual values of the arguments. This means that you should be very careful if you usememoization with something else than primitive or boxed primitive types.

The behavior of the cache can be tweaked using alternate methods:

  • memoizeAtMost will generate a new closure which cachesat mostn values

  • memoizeAtLeast will generate a new closure which cachesat leastn values

  • memoizeBetween will generate a new closure which cachesat leastn values andat mostn values

The cache used in all memoize variants is an LRU cache.

Composition

Closure composition corresponds to the concept of function composition, that is to say creating a new function bycomposing two or more functions (chaining calls), as illustrated in this example:

def plus2  = { it + 2 }def times3 = { it * 3 }def times3plus2 = plus2 << times3assert times3plus2(3) == 11assert times3plus2(4) == plus2(times3(4))def plus2times3 = times3 << plus2assert plus2times3(3) == 15assert plus2times3(5) == times3(plus2(5))// reverse compositionassert times3plus2(3) == (times3 >> plus2)(3)
Trampoline

Recursive algorithms are often restricted by a physical limit: the maximum stack height. For example, if you call a methodthat recursively calls itself too deep, you will eventually receive aStackOverflowException.

An approach that helps in those situations is by usingClosure and its trampoline capability.

Closures are wrapped in aTrampolineClosure. Upon calling, a trampolinedClosure will call the originalClosure waitingfor its result. If the outcome of the call is another instance of aTrampolineClosure, created perhaps as a resultto a call to thetrampoline() method, theClosure will again be invoked. This repetitive invocation of returnedtrampolined Closures instances will continue until a value other than a trampolinedClosure is returned. That valuewill become the final result of the trampoline. That way, calls are made serially, rather than filling the stack.

Here’s an example of the use oftrampoline() to implement the factorial function:

def factorialfactorial = { int n, def accu = 1G ->    if (n < 2) return accu    factorial.trampoline(n - 1, n * accu)}factorial = factorial.trampoline()assert factorial(1)    == 1assert factorial(3)    == 1 * 2 * 3assert factorial(1000) // == 402387260.. plus another 2560 digits
Method pointers

It is often practical to be able to use a regular method as a closure. For example, you might want to use the curryingabilities of a closure, but those are not available to normal methods. In Groovy, you can obtain a closure from anymethod with themethod pointer operator.

1.6. Semantics

This chapter covers the semantics of the Groovy programming language.

1.6.1. Statements

Variable definition

Variables can be defined using either their type (likeString) or by using the keyworddef (orvar) followed by a variable name:

String xdef yvar z

def andvar act as a type placeholder, i.e. a replacement for the type name,when you do not want to give an explicit type.It could be that you don’t care about the type at compile timeor are relying on type inference (with Groovy’s static nature).It is mandatory for variable definitions to have a type or placeholder.If left out, the type name will be deemed to refer to an existing variable (presumably declared earlier).For scripts, undeclared variables are assumed to come from the Script binding.In other cases, you will get a missing property (dynamic Groovy) or compile time error (static Groovy).If you think ofdef andvar as an alias ofObject, you will understand in an instant.

Variable definitions can provide an initial value,in which case it’s like having a declaration and assignment (which we cover next) all in one.

Variable definition types can be refined by using generics, like inList<String> names.To learn more about the generics support, please read thegenerics section.
Variable assignment

You can assign values to variables for later use. Try the following:

x = 1println xx = new java.util.Date()println xx = -3.1499392println xx = falseprintln xx = "Hi"println x
Multiple assignment

Groovy supports multiple assignment, i.e. where multiple variables can be assigned at once, e.g.:

def (a, b, c) = [10, 20, 'foo']assert a == 10 && b == 20 && c == 'foo'

You can provide types as part of the declaration if you wish:

def (int i, String j) = [10, 'foo']assert i == 10 && j == 'foo'

As well as used when declaring variables it also applies to existing variables:

def nums = [1, 3, 5]def a, b, c(a, b, c) = numsassert a == 1 && b == 3 && c == 5

The syntax works for arrays as well as lists, as well as methods that return either of these:

def (_, month, year) = "18th June 2009".split()assert "In $month of $year" == 'In June of 2009'
Overflow and Underflow

If the left hand side has too many variables, excess ones are filled with null’s:

def (a, b, c) = [1, 2]assert a == 1 && b == 2 && c == null

If the right hand side has too many variables, the extra ones are ignored:

def (a, b) = [1, 2, 3]assert a == 1 && b == 2
Object destructuring with multiple assignment

In the section describing Groovy’s operators,the case of thesubscript operator has been covered,explaining how you can override thegetAt()/putAt() method.

With this technique, we can combine multiple assignments and the subscript operator methods to implementobject destructuring.

Consider the following immutableCoordinates class, containing a pair of longitude and latitude doubles,and notice our implementation of thegetAt() method:

@Immutableclass Coordinates {    double latitude    double longitude    double getAt(int idx) {        if (idx == 0) latitude        else if (idx == 1) longitude        else throw new Exception("Wrong coordinate index, use 0 or 1")    }}

Now let’s instantiate this class and destructure its longitude and latitude:

def coordinates = new Coordinates(latitude: 43.23, longitude: 3.67)(1)def (la, lo) = coordinates(2)assert la == 43.23(3)assert lo == 3.67
1we create an instance of theCoordinates class
2then, we use a multiple assignment to get the individual longitude and latitude values
3and we can finally assert their values.
Control structures
Conditional structures
if / else

Groovy supports the usual if - else syntax from Java

def x = falsedef y = falseif ( !x ) {    x = true}assert x == trueif ( x ) {    x = false} else {    y = true}assert x == y

Groovy also supports the normal Java "nested" if then else if syntax:

if ( ... ) {    ...} else if (...) {    ...} else {    ...}
switch / case

The switch statement in Groovy is backwards compatible with Java code; so you can fall through cases sharing the same code for multiple matches.

One difference though is that the Groovy switch statement can handle any kind of switch value and different kinds of matching can be performed.

def x = 1.23def result = ""switch (x) {    case "foo":        result = "found foo"        // lets fall through    case "bar":        result += "bar"    case [4, 5, 6, 'inList']:        result = "list"        break    case 12..30:        result = "range"        break    case Integer:        result = "integer"        break    case Number:        result = "number"        break    case ~/fo*/: // toString() representation of x matches the pattern?        result = "foo regex"        break    case { it < 0 }: // or { x < 0 }        result = "negative"        break    default:        result = "default"}assert result == "number"

Switch supports the following kinds of comparisons:

  • Class case values match if the switch value is an instance of the class

  • Regular expression case values match if thetoString() representation of the switch value matches the regex

  • Collection case values match if the switch value is contained in the collection. This also includes ranges (since they are Lists)

  • Closure case values match if the calling the closure returns a result which is true according to theGroovy truth

  • If none of the above are used then the case value matches if the case value equals the switch value

When using a closure case value, the defaultit parameter is actually the switch value (in our example, variablex).

Groovy also supports switch expressions as shown in the following example:

def partner = switch(person) {    case 'Romeo'  -> 'Juliet'    case 'Adam'   -> 'Eve'    case 'Antony' -> 'Cleopatra'    case 'Bonnie' -> 'Clyde'}
Looping structures
Classic for loop

Groovy supports the standard Java / C for loop:

String message = ''for (int i = 0; i < 5; i++) {    message += 'Hi '}assert message == 'Hi Hi Hi Hi Hi '
Enhanced classic Java-style for loop

The more elaborate form of Java’s classic for loop with comma-separate expressionsis now supported. Example:

def facts = []def count = 5for (int fact = 1, i = 1; i <= count; i++, fact *= i) {    facts << fact}assert facts == [1, 2, 6, 24, 120]
Multi-assignment in combination with for loop

Groovy has supported multi-assignment statements since Groovy 1.6:

// multi-assignment with typesdef (String x, int y) = ['foo', 42]assert "$x $y" == 'foo 42'

These can now appear in for loops:

// multi-assignment goes loopydef baNums = []for (def (String u, int v) = ['bar', 42]; v < 45; u++, v++) {    baNums << "$u $v"}assert baNums == ['bar 42', 'bas 43', 'bat 44']
for in loop

The for loop in Groovy is much simpler and works with any kind of array, collection, Map, etc.

// iterate over a rangedef x = 0for ( i in 0..9 ) {    x += i}assert x == 45// iterate over a listx = 0for ( i in [0, 1, 2, 3, 4] ) {    x += i}assert x == 10// iterate over an arraydef array = (0..4).toArray()x = 0for ( i in array ) {    x += i}assert x == 10// iterate over a mapdef map = ['abc':1, 'def':2, 'xyz':3]x = 0for ( e in map ) {    x += e.value}assert x == 6// iterate over values in a mapx = 0for ( v in map.values() ) {    x += v}assert x == 6// iterate over the characters in a stringdef text = "abc"def list = []for (c in text) {    list.add(c)}assert list == ["a", "b", "c"]
Groovy also supports the Java colon variation with colons:for (char c : text) {}
while loop

Groovy supports the usual while {…​} loops like Java:

def x = 0def y = 5while ( y-- > 0 ) {    x++}assert x == 5
do/while loop

Java’s class do/while loop is now supported. Example:

// classic Java-style do..while loopdef count = 5def fact = 1do {    fact *= count--} while(count > 1)assert fact == 120
Exception handling

Exception handling is the same as Java.

try / catch / finally

You can specify a completetry-catch-finally, atry-catch, or atry-finally set of blocks.

Braces are required around each block’s body.
try {    'moo'.toLong()   // this will generate an exception    assert false     // asserting that this point should never be reached} catch ( e ) {    assert e in NumberFormatException}

We can put code within a 'finally' clause following a matching 'try' clause, so that regardless of whether the code in the 'try' clause throws an exception, the code in the finally clause will always execute:

def ztry {    def i = 7, j = 0    try {        def k = i / j        assert false        //never reached due to Exception in previous line    } finally {        z = 'reached here'  //always executed even if Exception thrown    }} catch ( e ) {    assert e in ArithmeticException    assert z == 'reached here'}
Multi-catch

With the multi catch block (since Groovy 2.0), we’re able to define several exceptions to be catch and treated by the same catch block:

try {    /* ... */} catch ( IOException | NullPointerException e ) {    /* one block to handle 2 exceptions */}
ARM Try with resources

Groovy often provides better alternatives to Java 7’stry-with-resources statement for Automatic Resource Management (ARM).That syntax is now supported for Java programmers migrating to Groovy and still wanting to use the old style:

class FromResource extends ByteArrayInputStream {    @Override    void close() throws IOException {        super.close()        println "FromResource closing"    }    FromResource(String input) {        super(input.toLowerCase().bytes)    }}class ToResource extends ByteArrayOutputStream {    @Override    void close() throws IOException {        super.close()        println "ToResource closing"    }}def wrestle(s) {    try (            FromResource from = new FromResource(s)            ToResource to = new ToResource()    ) {        to << from        return to.toString()    }}def wrestle2(s) {    FromResource from = new FromResource(s)    try (from; ToResource to = new ToResource()) { // Enhanced try-with-resources in Java 9+        to << from        return to.toString()    }}assert wrestle("ARM was here!").contains('arm')assert wrestle2("ARM was here!").contains('arm')

Which yields the following output:

ToResource closingFromResource closingToResource closingFromResource closing
Power assertion

Unlike Java with which Groovy shares theassert keyword, the latter in Groovy behaves very differently. First of all,an assertion in Groovy is always executed, independently of the-ea flag of the JVM. It makes this a first class choicefor unit tests. The notion of "power asserts" is directly related to how the Groovyassert behaves.

A power assertion is decomposed into 3 parts:

assert [left expression] == [right expression] : (optional message)

The result of the assertion is very different from what you would get in Java. If the assertion is true, then nothinghappens. If the assertion is false, then it provides a visual representation of the value of each sub-expressions of theexpression being asserted. For example:

assert 1+1 == 3

Will yield:

Caught: Assertion failed:assert 1+1 == 3        |  |        2  false

Power asserts become very interesting when the expressions are more complex, like in the next example:

def x = 2def y = 7def z = 5def calc = { a,b -> a*b+1 }assert calc(x,y) == [x,z].sum()

Which will print the value for each sub-expression:

assert calc(x,y) == [x,z].sum()       |    | |  |   | |  |       15   2 7  |   2 5  7                 false

In case you don’t want a pretty printed error message like above, you can fall back to a custom error message bychanging the optional message part of the assertion, like in this example:

def x = 2def y = 7def z = 5def calc = { a,b -> a*b+1 }assert calc(x,y) == z*z : 'Incorrect computation result'

Which will print the following error message:

Incorrect computation result. Expression: (calc.call(x, y) == (z * z)). Values: z = 5, z = 5
Labeled statements

Any statement can be associated with a label. Labels do not impact the semantics of the code and can be used to makethe code easier to read like in the following example:

given:    def x = 1    def y = 2when:    def z = x+ythen:    assert z == 3

Despite not changing the semantics of the labelled statement, it is possible to use labels in thebreak instructionas a target for jump, as in the next example. However, even if this is allowed, this coding style is in general considereda bad practice:

for (int i=0;i<10;i++) {    for (int j=0;j<i;j++) {        println "j=$j"        if (j == 5) {            break exit        }    }    exit: println "i=$i"}

It is important to understand that by default labels have no impact on the semantics of the code, however they belong to the abstractsyntax tree (AST) so it is possible for an AST transformation to use that information to perform transformations overthe code, hence leading to different semantics. This is in particular what theSpock Frameworkdoes to make testing easier.

1.6.2. Expressions

Expressions are the building blocks of Groovy programs that are used to referenceexisting values and execute code to create new ones.

Groovy supports many of the same kinds of expressions as Java, including:

Table 5. Expressions like Java

Example expression(s)

Description

foo

the name of a variable, field, parameter, …​

this,super,it

special names

true,10,"bar"

literals

String.class

Class literal

(expression)

parenthesised expressions

foo++,~bar

Unaryoperator expressions

foo + bar,bar * baz

Binaryoperator expressions

foo ? bar : baz

Ternaryoperator expressions

(Integer x, Integer y) → x + y

Lambda expressions

assert 'bar' == switch('foo') {  case 'foo' -> 'bar'}

switch expressions

Groovy also has some of its own special expressions:

Table 6. Special expressions

Example expression(s)

Description

String

Abbreviated class literal (when not ambiguous)

{ x, y → x + y }

Closure expressions

[1, 3, 5]

literal list expressions

[a:2, b:4, c:6]

literal map expressions

Groovy also expands on the normal dot-notation used in Java for member access.Groovy provides special support for accessing hierarchical data structures by specifying thepath in the hierarchy of some data of interest.TheseGroovy path expressions are known as GPath expressions.

GPath expressions

GPath is a path expression language integrated into Groovy which allows parts of nested structured data to be identified. In thissense, it has similar aims and scope as XPath does for XML. GPath is often used in the context of processing XML, but it really appliesto any object graph. Where XPath uses a filesystem-like path notation, a tree hierarchy with parts separated by a slash/, GPathuse adot-object notation to perform object navigation.

As an example, you can specify a path to an object or element of interest:

  • a.b.c → for XML, yields all thec elements insideb insidea

  • a.b.c → for POJOs, yields thec properties for all theb properties ofa (sort of likea.getB().getC() in JavaBeans)

In both cases, the GPath expression can be viewed as a query on an object graph. For POJOs, the object graph is most often built by theprogram being written through object instantiation and composition; for XML processing, the object graph is the result ofparsingthe XML text, most often with classes like XmlParser or XmlSlurper. SeeProcessing XMLfor more in-depth details on consuming XML in Groovy.

When querying the object graph generated from XmlParser or XmlSlurper, a GPath expression can refer to attributes defined on elements withthe@ notation:

  • a["@href"] → map-like notation : the href attribute of all the a elements

  • a.'@href' → property notation : an alternative way of expressing this

  • a.@href → direct notation : yet another alternative way of expressing this

Object navigation

Let’s see an example of a GPath expression on a simpleobject graph, the one obtained using java reflection. Suppose you are in a non-static method of aclass having another method namedaMethodFoo

void aMethodFoo() { println "This is aMethodFoo." }(0)

the following GPath expression will get the name of that method:

assert ['aMethodFoo'] == this.class.methods.name.grep(~/.*Foo/)

More precisely, the above GPath expression produces a list of String, each being the name of an existing method onthis where that name ends withFoo.

Now, given the following methods also defined in that class:

void aMethodBar() { println "This is aMethodBar." }(1)void anotherFooMethod() { println "This is anotherFooMethod." }(2)void aSecondMethodBar() { println "This is aSecondMethodBar." }(3)

then the following GPath expression will get the names of(1) and(3), but not(2) or(0):

assert ['aMethodBar', 'aSecondMethodBar'] as Set == this.class.methods.name.grep(~/.*Bar/) as Set
Expression Deconstruction

We can decompose the expressionthis.class.methods.name.grep(~/.*Bar/) to get an idea of how a GPath is evaluated:

this.class

property accessor, equivalent tothis.getClass() in Java, yields aClass object.

this.class.methods

property accessor, equivalent tothis.getClass().getMethods(), yields an array ofMethod objects.

this.class.methods.name

apply a property accessor on each element of an array and produce a list of the results.

this.class.methods.name.grep(…​)

call methodgrep on each element of the list yielded bythis.class.methods.name and produce a list of the results.

A sub-expression likethis.class.methods yields an array because this is what callingthis.getClass().getMethods() in Javawould produce.GPath expressions do not have a convention where as means a list or anything like that.

One powerful feature of GPath expression is that property access on a collection is converted to aproperty access on each element of the collection withthe results collected into a collection. Therefore, the expressionthis.class.methods.name could be expressed as follows in Java:

List<String> methodNames = new ArrayList<String>();for (Method method : this.getClass().getMethods()) {   methodNames.add(method.getName());}return methodNames;

Array access notation can also be used in a GPath expression where a collection is present :

assert 'aSecondMethodBar' == this.class.methods.name.grep(~/.*Bar/).sort()[1]
array access are zero-based in GPath expressions
GPath for XML navigation

Here is an example with an XML document and various form of GPath expressions:

def xmlText = """              | <root>              |   <level>              |      <sublevel id='1'>              |        <keyVal>              |          <key>mykey</key>              |          <value>value 123</value>              |        </keyVal>              |      </sublevel>              |      <sublevel id='2'>              |        <keyVal>              |          <key>anotherKey</key>              |          <value>42</value>              |        </keyVal>              |        <keyVal>              |          <key>mykey</key>              |          <value>fizzbuzz</value>              |        </keyVal>              |      </sublevel>              |   </level>              | </root>              """def root = new XmlSlurper().parseText(xmlText.stripMargin())assert root.level.size() == 1(1)assert root.level.sublevel.size() == 2(2)assert root.level.sublevel.findAll { it.@id == 1 }.size() == 1(3)assert root.level.sublevel[1].keyVal[0].key.text() == 'anotherKey'(4)
1There is onelevel node underroot
2There are twosublevel nodes underroot/level
3There is one elementsublevel having an attributeid with value1
4Text value ofkey element of firstkeyVal element of secondsublevel element underroot/level is 'anotherKey'

Further details about GPath expressions for XML are in theXML User Guide.

1.6.3. Promotion and coercion

Number promotion

The rules of number promotion are specified in the section onmath operations.

Closure to type coercion
Assigning a closure to a SAM type

A SAM type is a type which defines a single abstract method. This includes:

Functional interfaces
interface Predicate<T> {    boolean accept(T obj)}
Abstract classes with single abstract method
abstract class Greeter {    abstract String getName()    void greet() {        println "Hello, $name"    }}

Any closure can be converted into a SAM type using theas operator:

Predicate filter = { it.contains 'G' } as Predicateassert filter.accept('Groovy') == trueGreeter greeter = { 'Groovy' } as Greetergreeter.greet()

However, theas Type expression is optional since Groovy 2.2.0. You can omit it and simply write:

Predicate filter = { it.contains 'G' }assert filter.accept('Groovy') == trueGreeter greeter = { 'Groovy' }greeter.greet()

which means you are also allowed to use method pointers, as shown in the following example:

boolean doFilter(String s) { s.contains('G') }Predicate filter = this.&doFilterassert filter.accept('Groovy') == trueGreeter greeter = GroovySystem.&getVersiongreeter.greet()
Calling a method accepting a SAM type with a closure

The second and probably more important use case for closure to SAM type coercion is calling a method which acceptsa SAM type. Imagine the following method:

public <T> List<T> filter(List<T> source, Predicate<T> predicate) {    source.findAll { predicate.accept(it) }}

Then you can call it with a closure, without having to create an explicit implementation of the interface:

assert filter(['Java','Groovy'], { it.contains 'G'} as Predicate) == ['Groovy']

But since Groovy 2.2.0, you are also able to omit the explicit coercion and call the method as if it used a closure:

assert filter(['Java','Groovy']) { it.contains 'G'} == ['Groovy']

As you can see, this has the advantage of letting you use the closure syntax for method calls, that is to say put theclosure outside the parenthesis, improving the readability of your code.

Closure to arbitrary type coercion

In addition to SAM types, a closure can be coerced to any type and in particular interfaces. Let’s define thefollowing interface:

interface FooBar {    int foo()    void bar()}

You can coerce a closure into the interface using theas keyword:

def impl = { println 'ok'; 123 } as FooBar

This produces a class for which all methods are implemented using the closure:

assert impl.foo() == 123impl.bar()

But it is also possible to coerce a closure to any class. For example, we can replace theinterface that we definedwithclass without changing the assertions:

class FooBar {    int foo() { 1 }    void bar() { println 'bar' }}def impl = { println 'ok'; 123 } as FooBarassert impl.foo() == 123impl.bar()
Map to type coercion

Usually using a single closure to implement an interface or a class with multiple methods is not the way to go. As analternative, Groovy allows you to coerce a map into an interface or a class. In that case, keys of the map areinterpreted as method names, while the values are the method implementation. The following example illustrates thecoercion of a map into anIterator:

def mapmap = [  i: 10,  hasNext: { map.i > 0 },  next: { map.i-- },]def iter = map as Iterator

Of course this is a rather contrived example, but illustrates the concept. You only need to implement those methodsthat are actually called, but if a method is called that doesn’t exist in the map aMissingMethodException or anUnsupportedOperationException is thrown, depending on the arguments passed to the call,as in the following example:

interface X {    void f()    void g(int n)    void h(String s, int n)}x = [ f: {println "f called"} ] as Xx.f() // method existsx.g() // MissingMethodException herex.g(5) // UnsupportedOperationException here

The type of the exception depends on the call itself:

  • MissingMethodException if the arguments of the call do not match those from the interface/class

  • UnsupportedOperationException if the arguments of the call match one of the overloaded methods of the interface/class

String to enum coercion

Groovy allows transparentString (orGString) to enum values coercion. Imagine you define the following enum:

enum State {    up,    down}

then you can assign a string to the enum without having to use an explicitas coercion:

State st = 'up'assert st == State.up

It is also possible to use aGString as the value:

def val = "up"State st = "${val}"assert st == State.up

However, this would throw a runtime error (IllegalArgumentException):

State st = 'not an enum value'

Note that it is also possible to use implicit coercion in switch statements:

State switchState(State st) {    switch (st) {        case 'up':            return State.down // explicit constant        case 'down':            return 'up' // implicit coercion for return types    }}

in particular, see how thecase use string constants. But if you call a method that uses an enum with aStringargument, you still have to use an explicitas coercion:

assert switchState('up' as State) == State.downassert switchState(State.down) == State.up
Custom type coercion

It is possible for a class to define custom coercion strategies by implementing theasType method. Custom coercionis invoked using theas operator and is never implicit. As an example,imagine you defined two classes,Polar andCartesian, like in the following example:

class Polar {    double r    double phi}class Cartesian {   double x   double y}

And that you want to convert from polar coordinates to cartesian coordinates. One way of doing this is to definetheasType method in thePolar class:

def asType(Class target) {    if (Cartesian==target) {        return new Cartesian(x: r*cos(phi), y: r*sin(phi))    }}

which allows you to use theas coercion operator:

def sigma = 1E-16def polar = new Polar(r:1.0,phi:PI/2)def cartesian = polar as Cartesianassert abs(cartesian.x-sigma) < sigma

Putting it all together, thePolar class looks like this:

class Polar {    double r    double phi    def asType(Class target) {        if (Cartesian==target) {            return new Cartesian(x: r*cos(phi), y: r*sin(phi))        }    }}

but it is also possible to defineasType outside of thePolar class, which can be practical if you want to definecustom coercion strategies for "closed" classes or classes for which you don’t own the source code, for example usinga metaclass:

Polar.metaClass.asType = { Class target ->    if (Cartesian==target) {        return new Cartesian(x: r*cos(phi), y: r*sin(phi))    }}
Class literals vs variables and the as operator

Using theas keyword is only possible if you have a static reference to a class, like in the following code:

interface Greeter {    void greet()}def greeter = { println 'Hello, Groovy!' } as Greeter // Greeter is known staticallygreeter.greet()

But what if you get the class by reflection, for example by callingClass.forName?

Class clazz = Class.forName('Greeter')

Trying to use the reference to the class with theas keyword would fail:

greeter = { println 'Hello, Groovy!' } as clazz// throws:// unable to resolve class clazz// @ line 9, column 40.//   greeter = { println 'Hello, Groovy!' } as clazz

It is failing because theas keyword only works with class literals. Instead, you need to call theasType method:

greeter = { println 'Hello, Groovy!' }.asType(clazz)greeter.greet()

1.6.4. Optionality

Optional parentheses

Method calls can omit the parentheses if there is at least one parameter and there is no ambiguity:

println 'Hello World'def maximum = Math.max 5, 10

Parentheses are required for method calls without parameters or ambiguous method calls:

println()println(Math.max(5, 10))
Optional semicolons

In Groovy semicolons at the end of the line can be omitted, if the line contains only a single statement.

This means that:

assert true;

can be more idiomatically written as:

assert true

Multiple statements in a line require semicolons to separate them:

boolean a = true; assert a
Optional return keyword

In Groovy, the last expression evaluated in the body of a method or a closure is returned. This means that thereturn keyword is optional.

int add(int a, int b) {    return a+b}assert add(1, 2) == 3

Can be shortened to:

int add(int a, int b) {    a+b}assert add(1, 2) == 3
Optional public keyword

By default, Groovy classes and methods arepublic. Therefore this class:

public class Server {    public String toString() { "a server" }}

is identical to this class:

class Server {    String toString() { "a server" }}

1.6.5. The Groovy Truth

Groovy decides whether an expression is true or false by applying the rules given below.

Boolean expressions

True if the corresponding Boolean value istrue.

assert trueassert !false
Collections and Arrays

Non-empty Collections and arrays are true.

assert [1, 2, 3]assert ![]
Matchers

True if the Matcher has at least one match.

assert ('a' =~ /a/)assert !('a' =~ /b/)
Iterators and Enumerations

Iterators and Enumerations with further elements are coerced to true.

assert [0].iterator()assert ![].iterator()Vector v = [0] as VectorEnumeration enumeration = v.elements()assert enumerationenumeration.nextElement()assert !enumeration
Maps

Non-empty Maps are evaluated to true.

assert ['one' : 1]assert ![:]
Strings

Non-empty Strings, GStrings and CharSequences are coerced to true.

assert 'a'assert !''def nonEmpty = 'a'assert "$nonEmpty"def empty = ''assert !"$empty"
Numbers

Non-zero numbers are true.

assert 1assert 3.5assert !0
Object References

Non-null object references are coerced to true.

assert new Object()assert !null
Customizing the truth with asBoolean() methods

In order to customize whether groovy evaluates your object totrue orfalse implement theasBoolean() method:

class Color {    String name    boolean asBoolean(){        name == 'green' ? true : false     }}

Groovy will call this method to coerce your object to a boolean value, e.g.:

assert new Color(name: 'green')assert !new Color(name: 'red')

1.6.6. Typing

Optional typing

Optional typing is the idea that a program can work even if you don’t put an explicit type on a variable. Being a dynamiclanguage, Groovy naturally implements that feature, for example when you declare a variable:

String aString = 'foo'(1)assert aString.toUpperCase()(2)
1foo is declared using an explicit type,String
2we can call thetoUpperCase method on aString

Groovy will let you write this instead:

def aString = 'foo'(1)assert aString.toUpperCase()(2)
1foo is declared usingdef
2we can still call thetoUpperCase method, because the type ofaString is resolved at runtime

So it doesn’t matter that you use an explicit type here. It is in particular interesting when you combine this featurewithstatic type checking, because the type checker performs type inference.

Likewise, Groovy doesn’t make it mandatory to declare the types of a parameter in a method:

String concat(String a, String b) {    a+b}assert concat('foo','bar') == 'foobar'

can be rewritten usingdef as both return type and parameter types, in order to take advantage of duck typing, asillustrated in this example:

def concat(def a, def b) {(1)    a+b}assert concat('foo','bar') == 'foobar'(2)assert concat(1,2) == 3(3)
1both the return type and the parameter types usedef
2it makes it possible to use the method withString
3but also withint since theplus method is defined
Using thedef keyword here is recommended to describe the intent of a method which is supposed to work on anytype, but technically, we could useObject instead and the result would be the same:def is, in Groovy, strictlyequivalent to usingObject.

Eventually, the type can be removed altogether from both the return type and the descriptor. But if you want to removeit from the return type, you then need to add an explicit modifier for the method, so that the compiler can make a differencebetween a method declaration and a method call, like illustrated in this example:

private concat(a,b) {(1)    a+b}assert concat('foo','bar') == 'foobar'(2)assert concat(1,2) == 3(3)
1if we want to omit the return type, an explicit modifier has to be set.
2it is still possible to use the method withString
3and also withint
Omitting types is in general considered a bad practice in method parameters or method return types for public APIs.While usingdef in a local variable is not really a problem because the visibility of the variable is limited to themethod itself, while set on a method parameter,def will be converted toObject in the method signature, making itdifficult for users to know which is the expected type of the arguments. This means that you should limit this to caseswhere you are explicitly relying on duck typing.
Static type checking

By default, Groovy performs minimal type checking at compile time. Since it is primarily a dynamic language,most checks that a static compiler would normally do aren’t possible at compile time. A method added via runtimemetaprogramming might alter a class or object’s runtime behavior. Let’s illustrate why in thefollowing example:

class Person {(1)    String firstName    String lastName}def p = new Person(firstName: 'Raymond', lastName: 'Devos')(2)assert p.formattedName == 'Raymond Devos'(3)
1thePerson class only defines two properties,firstName andlastName
2we can create an instance of Person
3and call a method namedformattedName

It is quite common in dynamic languages for code such as the above example not to throw any error. How can this be?In Java, this would typically fail at compile time. However, in Groovy, it will not fail at compile time, and if codedcorrectly, will also not fail at runtime. In fact, to make this work at runtime,one possibility is to rely onruntime metaprogramming. So just adding this line after the declaration of thePerson class is enough:

Person.metaClass.getFormattedName = { "$delegate.firstName $delegate.lastName" }

This means that in general, in Groovy, you can’t make any assumption about the type of an object beyond its declarationtype, and even if you know it, you can’t determine at compile time what method will be called, or which property willbe retrieved. It has a lot of interest, going from writing DSLs to testing, which is discussed in other sections of thismanual.

However, if your program doesn’t rely on dynamic features and that you come from the static world (in particular, froma Java mindset), not catching such "errors" at compile time can be surprising. As we have seen in the previous example,the compiler cannot be sure this is an error. To make it aware that it is, you have to explicitly instruct the compilerthat you are switching to a type checked mode. This can be done by annotating a class or a method with@groovy.transform.TypeChecked.

When type checking is activated, the compiler performs much more work:

  • type inference is activated, meaning that even if you usedef on a local variable for example, the type checker will beable to infer the type of the variable from the assignments

  • method calls are resolved at compile time, meaning that if a method is not declared on a class, the compiler will throw an error

  • in general, all the compile time errors that you are used to find in a static language will appear: method not found, property not found,incompatible types for method calls, number precision errors, …​

In this section, we will describe the behavior of the type checker in various situations and explain the limits of using@TypeChecked on your code.

The@TypeChecked annotation
Activating type checking at compile time

Thegroovy.transform.TypeChecked annotation enables type checking. It can be placed on a class:

@groovy.transform.TypeCheckedclass Calculator {    int sum(int x, int y) { x+y }}

Or on a method:

class Calculator {    @groovy.transform.TypeChecked    int sum(int x, int y) { x+y }}

In the first case, all methods, properties, fields, inner classes, …​ of the annotated class will be type checked, whereasin the second case, only the method and potential closures or anonymous inner classes that it contains will be type checked.

Skipping sections

The scope of type checking can be restricted. For example, if a class is type checked, you can instruct the type checkerto skip a method by annotating it with@TypeChecked(TypeCheckingMode.SKIP):

import groovy.transform.TypeCheckedimport groovy.transform.TypeCheckingMode@TypeChecked(1)class GreetingService {    String greeting() {(2)        doGreet()    }    @TypeChecked(TypeCheckingMode.SKIP)(3)    private String doGreet() {        def b = new SentenceBuilder()        b.Hello.my.name.is.John(4)        b    }}def s = new GreetingService()assert s.greeting() == 'Hello my name is John'
1theGreetingService class is marked as type checked
2so thegreeting method is automatically type checked
3butdoGreet is marked withSKIP
4the type checker doesn’t complain about missing properties here

In the previous example,SentenceBuilder relies on dynamic code. There’s no realHello method or property, so thetype checker would normally complain and compilation would fail. Since the method that uses the builder is marked withTypeCheckingMode.SKIP, type checking isskipped for this method, so the code will compile, even if the rest of theclass is type checked.

The following sections describe the semantics of type checking in Groovy.

Type checking assignments

An objecto of typeA can be assigned to a variable of typeT if and only if:

  • T equalsA

    Date now = new Date()
  • orT is one ofString,boolean,Boolean orClass

    String s = new Date() // implicit call to toStringBoolean boxed = 'some string'       // Groovy truthboolean prim = 'some string'        // Groovy truthClass clazz = 'java.lang.String'    // class coercion
  • oro is null andT is not a primitive type

    String s = null         // passesint i = null            // fails
  • orT is an array andA is an array and the component type ofA is assignable to the component type ofT

    int[] i = new int[4]        // passesint[] i = new String[4]     // fails
  • orT is an array andA is a collection or stream and the component type ofA is assignable to the component type ofT

    int[] i = [1,2,3]               // passesint[] i = [1,2, new Date()]     // failsSet set = [1,2,3]Number[] na = set               // passesdef stream = Arrays.stream(1,2,3)int[] i = stream                // passes
  • orT is a superclass ofA

    AbstractList list = new ArrayList()     // passesLinkedList list = new ArrayList()       // fails
  • orT is an interface implemented byA

    List list = new ArrayList()             // passesRandomAccess list = new LinkedList()    // fails
  • orT orA are a primitive type and their boxed types are assignable

    int i = 0Integer bi = 1int x = Integer.valueOf(123)double d = Float.valueOf(5f)
  • orT extendsgroovy.lang.Closure andA is a SAM-type (single abstract method type)

    Runnable r = { println 'Hello' }interface SAMType {    int doSomething()}SAMType sam = { 123 }assert sam.doSomething() == 123abstract class AbstractSAM {    int calc() { 2* value() }    abstract int value()}AbstractSAM c = { 123 }assert c.calc() == 246
  • orT andA derive fromjava.lang.Number and conform to the following table

Table 7. Number types (java.lang.XXX)
TAExamples

Double

Any but BigDecimal or BigInteger

Double d1 = 4dDouble d2 = 4fDouble d3 = 4lDouble d4 = 4iDouble d5 = (short) 4Double d6 = (byte) 4

Float

Any type but BigDecimal, BigInteger or Double

Float f1 = 4fFloat f2 = 4lFloat f3 = 4iFloat f4 = (short) 4Float f5 = (byte) 4

Long

Any type but BigDecimal, BigInteger, Double or Float

Long l1 = 4lLong l2 = 4iLong l3 = (short) 4Long l4 = (byte) 4

Integer

Any type but BigDecimal, BigInteger, Double, Float or Long

Integer i1 = 4iInteger i2 = (short) 4Integer i3 = (byte) 4

Short

Any type but BigDecimal, BigInteger, Double, Float, Long or Integer

Short s1 = (short) 4Short s2 = (byte) 4

Byte

Byte

Byte b1 = (byte) 4
List and map constructors

In addition to the assignment rules above, if an assignment is deemed invalid, in type checked mode, alist literal or amap literalA can be assignedto a variable of typeT if:

  • the assignment is a variable declaration andA is a list literal andT has a constructor whose parameters match the types of the elements in the list literal

  • the assignment is a variable declaration andA is a map literal andT has a no-arg constructor and a property for each of the map keys

For example, instead of writing:

@groovy.transform.TupleConstructorclass Person {    String firstName    String lastName}Person classic = new Person('Ada','Lovelace')

You can use a "list constructor":

Person list = ['Ada','Lovelace']

or a "map constructor":

Person map = [firstName:'Ada', lastName:'Lovelace']

If you use a map constructor, additional checks are done on the keys of the map to check if a property of the same nameis defined. For example, the following will fail at compile time:

@groovy.transform.TupleConstructorclass Person {    String firstName    String lastName}Person map = [firstName:'Ada', lastName:'Lovelace', age: 24](1)
1The type checker will throw an errorNo such property: age for class: Person at compile time
Method resolution

In type checked mode, methods are resolved at compile time. Resolution works by name and arguments. The return type isirrelevant to method selection. Types of arguments are matched against the types of the parameters following those rules:

An argumento of typeA can be used for a parameter of typeT if and only if:

  • T equalsA

    int sum(int x, int y) {    x+y}assert sum(3,4) == 7
  • orT is aString andA is aGString

    String format(String str) {    "Result: $str"}assert format("${3+4}") == "Result: 7"
  • oro is null andT is not a primitive type

    String format(int value) {    "Result: $value"}assert format(7) == "Result: 7"format(null)           // fails
  • orT is an array andA is an array and the component type ofA is assignable to the component type ofT

    String format(String[] values) {    "Result: ${values.join(' ')}"}assert format(['a','b'] as String[]) == "Result: a b"format([1,2] as int[])              // fails
  • orT is a superclass ofA

    String format(AbstractList list) {    list.join(',')}format(new ArrayList())              // passesString format(LinkedList list) {    list.join(',')}format(new ArrayList())              // fails
  • orT is an interface implemented byA

    String format(List list) {    list.join(',')}format(new ArrayList())                  // passesString format(RandomAccess list) {    'foo'}format(new LinkedList())                 // fails
  • orT orA are a primitive type and their boxed types are assignable

    int sum(int x, Integer y) {    x+y}assert sum(3, new Integer(4)) == 7assert sum(new Integer(3), 4) == 7assert sum(new Integer(3), new Integer(4)) == 7assert sum(new Integer(3), 4) == 7
  • orT extendsgroovy.lang.Closure andA is a SAM-type (single abstract method type)

    interface SAMType {    int doSomething()}int twice(SAMType sam) { 2*sam.doSomething() }assert twice { 123 } == 246abstract class AbstractSAM {    int calc() { 2* value() }    abstract int value()}int eightTimes(AbstractSAM sam) { 4*sam.calc() }assert eightTimes { 123 } == 984
  • orT andA derive fromjava.lang.Number and conform to the same rules asassignment of numbers

If a method with the appropriate name and arguments is not found at compile time, an error is thrown. The difference with "normal" Groovy isillustrated in the following example:

class MyService {    void doSomething() {        printLine 'Do something'(1)    }}
1printLine is an error, but since we’re in a dynamic mode, the error is not caught at compile time

The example above shows a class that Groovy will be able to compile. However, if you try to create an instance ofMyService and call thedoSomething method, then it will failat runtime, becauseprintLine doesn’t exist. Of course, we already showed how Groovy could makethis a perfectly valid call, for example by catchingMethodMissingException or implementing a custom metaclass, but if you know you’renot in such a case,@TypeChecked comes handy:

@groovy.transform.TypeCheckedclass MyService {    void doSomething() {        printLine 'Do something'(1)    }}
1printLine is this time a compile-time error

Just adding@TypeChecked will trigger compile time method resolution. The type checker will try to find a methodprintLine acceptingaString on theMyService class, but cannot find one. It will fail compilation with the following message:

Cannot find matching method MyService#printLine(java.lang.String)

It is important to understand the logic behind the type checker: it is a compile-time check, so by definition, the type checkeris not aware of any kind ofruntime metaprogramming that you do. This means that code which is perfectly valid without@TypeChecked willnot compile anymore if you activate type checking. This is in particular true if you think of duck typing:
class Duck {    void quack() {(1)        println 'Quack!'    }}class QuackingBird {    void quack() {(2)        println 'Quack!'    }}@groovy.transform.TypeCheckedvoid accept(quacker) {    quacker.quack()(3)}accept(new Duck())(4)
1we define aDuck class which defines aquack method
2we define anotherQuackingBird class which also defines aquack method
3quacker is loosely typed, so since the method is@TypeChecked, we will obtain a compile-time error
4even if in non type-checked Groovy, this would have passed

There are possible workarounds, like introducing an interface, but basically, by activating type checking, you gain type safetybut you loose some features of the language. Hopefully, Groovy introduces some features like flow typing to reduce the gap betweentype-checked and non type-checked Groovy.

Type inference
Principles

When code is annotated with@TypeChecked, the compiler performs type inference. It doesn’t simply rely on static types, but also uses varioustechniques to infer the types of variables, return types, literals, …​ so that the code remains as clean as possible even if you activate thetype checker.

The simplest example is inferring the type of a variable:

def message = 'Welcome to Groovy!'(1)println message.toUpperCase()(2)println message.upper() // compile time error(3)
1a variable is declared using thedef keyword
2callingtoUpperCase is allowed by the type checker
3callingupper will fail at compile time

The reason the call totoUpperCase works is because the type ofmessage wasinferred as being aString.

Variables vs fields in type inference

It is worth noting that although the compiler performs type inference on local variables, it doesnot perform any kindof type inference on fields, always falling back to thedeclared type of a field. To illustrate this, let’s take alook at this example:

class SomeClass {    def someUntypedField(1)    String someTypedField(2)    void someMethod() {        someUntypedField = '123'(3)        someUntypedField = someUntypedField.toUpperCase()  // compile-time error(4)    }    void someSafeMethod() {        someTypedField = '123'(5)        someTypedField = someTypedField.toUpperCase()(6)    }    void someMethodUsingLocalVariable() {        def localVariable = '123'(7)        someUntypedField = localVariable.toUpperCase()(8)    }}
1someUntypedField usesdef as a declaration type
2someTypedField usesString as a declaration type
3we can assignanything tosomeUntypedField
4yet callingtoUpperCase fails at compile time because the field is not typed properly
5we can assign aString to a field of typeString
6and this timetoUpperCase is allowed
7if we assign aString to a local variable
8then callingtoUpperCase is allowed on the local variable

Why such a difference? The reason isthread safety. At compile time, we can’t makeany guarantee about the type ofa field. Any thread can access any field at any time and between the moment a field is assigned a variable of sometype in a method and the time is used the line after, another thread may have changed the contents of the field. Thisis not the case for local variables: we know if they "escape" or not, so we can make sure that the type of a variable isconstant (or not) over time. Note that even if a field is final, the JVM makes no guarantee about it, so the type checkerdoesn’t behave differently if a field is final or not.

This is one of the reasons why we recommend to usetyped fields. While usingdef for local variables is perfectlyfine thanks to type inference, this is not the case for fields, which also belong to the public API of a class, hence thetype is important.
Collection literal type inference

Groovy provides a syntax for various type literals. There are three native collection literals in Groovy:

  • lists, using the[] literal

  • maps, using the[:] literal

  • ranges, usingfrom..to (inclusive),from..<to (right exclusive),from<..to (left exclusive) andfrom<..<to (full exclusive)

The inferred type of a literal depends on the elements of the literal, as illustrated in the following table:

LiteralInferred type
def list = []

java.util.List

def list = ['foo','bar']

java.util.List<String>

def list = ["${foo}","${bar}"]

java.util.List<GString> be careful, aGString isnot aString!

def map = [:]

java.util.LinkedHashMap

def map1 = [someKey: 'someValue']def map2 = ['someKey': 'someValue']

java.util.LinkedHashMap<String,String>

def map = ["${someKey}": 'someValue']

java.util.LinkedHashMap<GString,String> be careful, the key is aGString!

def intRange = (0..10)

groovy.lang.IntRange

def charRange = ('a'..'z')

groovy.lang.Range<String> : uses the type of the bounds to infer the component type of the range

As you can see, with the noticeable exception of theIntRange, the inferred type makes use of generics types to describethe contents of a collection. In case the collection contains elements of different types, the type checker still performstype inference of the components, but uses the notion ofleast upper bound.

Least upper bound

In Groovy, theleast upper bound of two typesA andB is defined as a type which:

  • superclass corresponds to the common super class ofA andB

  • interfaces correspond to the interfaces implemented by bothA andB

  • ifA orB is a primitive type and thatA isn’t equal toB, the least upper bound ofA andB is the leastupper bound of their wrapper types

IfA andB only have one (1) interface in common and that their common superclass isObject, then the LUB of bothis the common interface.

The least upper bound represents the minimal type to which bothA andB can be assigned. So for example, ifA andBare bothString, then the LUB (least upper bound) of both is alsoString.

class Top {}class Bottom1 extends Top {}class Bottom2 extends Top {}assert leastUpperBound(String, String) == String(1)assert leastUpperBound(ArrayList, LinkedList) == AbstractList(2)assert leastUpperBound(ArrayList, List) == List(3)assert leastUpperBound(List, List) == List(4)assert leastUpperBound(Bottom1, Bottom2) == Top(5)assert leastUpperBound(List, Serializable) == Object(6)
1the LUB ofString andString isString
2the LUB ofArrayList andLinkedList is their common super type,AbstractList
3the LUB ofArrayList andList is their only common interface,List
4the LUB of two identical interfaces is the interface itself
5the LUB ofBottom1 andBottom2 is their superclassTop
6the LUB of two types which have nothing in common isObject

In those examples, the LUB is always representable as a normal, JVM supported, type. But Groovy internally represents the LUBas a type which can be more complex, and that you wouldn’t be able to use to define a variable for example. To illustrate this,let’s continue with this example:

interface Foo {}class Top {}class Bottom extends Top implements Serializable, Foo {}class SerializableFooImpl implements Serializable, Foo {}

What is the least upper bound ofBottom andSerializableFooImpl? They don’t have a common super class (apart fromObject),but they do share 2 interfaces (Serializable andFoo), so their least upper bound is a type which represents the union oftwo interfaces (Serializable andFoo). This type cannot be defined in the source code, yet Groovy knows about it.

In the context of collection type inference (and generic type inference in general), this becomes handy, because the type of thecomponents is inferred as the least upper bound. We can illustrate why this is important in the following example:

interface Greeter { void greet() }(1)interface Salute { void salute() }(2)class A implements Greeter, Salute {(3)    void greet() { println "Hello, I'm A!" }    void salute() { println "Bye from A!" }}class B implements Greeter, Salute {(4)    void greet() { println "Hello, I'm B!" }    void salute() { println "Bye from B!" }    void exit() { println 'No way!' }(5)}def list = [new A(), new B()](6)list.each {    it.greet()(7)    it.salute()(8)    it.exit()(9)}
1theGreeter interface defines a single method,greet
2theSalute interface defines a single method,salute
3classA implements bothGreeter andSalute but there’s no explicit interface extending both
4same forB
5butB defines an additionalexit method
6the type oflist is inferred as "list of the LUB ofA and `B`"
7so it is possible to callgreet which is defined on bothA andB through theGreeter interface
8and it is possible to callsalute which is defined on bothA andB through theSalute interface
9yet callingexit is a compile time error because it doesn’t belong to the LUB ofA andB (only defined inB)

The error message will look like:

[Static type checking] - Cannot find matching method Greeter or Salute#exit()

which indicates that theexit method is neither defines onGreeter norSalute, which are the two interfaces definedin the least upper bound ofA andB.

instanceof inference

In normal, non type checked, Groovy, you can write things like:

class Greeter {    String greeting() { 'Hello' }}void doSomething(def o) {    if (o instanceof Greeter) {(1)        println o.greeting()(2)    }}doSomething(new Greeter())
1guard the method call with aninstanceof check
2make the call

The method call works because of dynamic dispatch (the method is selected at runtime). The equivalent code in Java wouldrequire to casto to aGreeter before calling thegreeting method, because methods are selected at compile time:

if (o instanceof Greeter) {    System.out.println(((Greeter)o).greeting());}

However, in Groovy, even if you add@TypeChecked (and thus activate type checking) on thedoSomething method, thecast isnot necessary. The compiler embedsinstanceof inference that makes the cast optional.

Flow typing

Flow typing is an important concept of Groovy in type checked mode and an extension of type inference. The idea is thatthe compiler is capable of inferring the type of variables in the flow of the code, not just at initialization:

@groovy.transform.TypeCheckedvoid flowTyping() {    def o = 'foo'(1)    o = o.toUpperCase()(2)    o = 9d(3)    o = Math.sqrt(o)(4)}
1first,o is declared usingdef and assigned aString
2the compiler inferred thato is aString, so callingtoUpperCase is allowed
3o is reassigned with adouble
4callingMath.sqrt passes compilation because the compiler knows that at this point,o is adouble

So the type checker isaware of the fact that the concrete type of a variable is different over time. In particular,if you replace the last assignment with:

o = 9do = o.toUpperCase()

The type checker will now fail at compile time, because it knows thato is adouble whentoUpperCase is called,so it’s a type error.

It is important to understand that it is not the fact of declaring a variable withdef that triggers type inference.Flow typing works forany variable of any type. Declaring a variable with an explicit type only constrains what youcan assign to the variable:

@groovy.transform.TypeCheckedvoid flowTypingWithExplicitType() {    List list = ['a','b','c'](1)    list = list*.toUpperCase()(2)    list = 'foo'(3)}
1list is declared as an uncheckedList and assigned a list literal of `String`s
2this line passes compilation because of flow typing: the type checker knows thatlist is at this point aList<String>
3but you can’t assign aString to aList so this is a type checking error

You can also note that even if the variable is declaredwithout generics information, the type checker knows what isthe component type. Therefore, such code would fail compilation:

@groovy.transform.TypeCheckedvoid flowTypingWithExplicitType() {    List list = ['a','b','c'](1)    list.add(1)(2)}
1list is inferred asList<String>
2so adding anint to aList<String> is a compile-time error

Fixing this requires adding an explicit generic type to the declaration:

@groovy.transform.TypeCheckedvoid flowTypingWithExplicitType() {    List<? extends Serializable> list = [](1)    list.addAll(['a','b','c'])(2)    list.add(1)(3)}
1list declared asList<? extends Serializable> and initialized with an empty list
2elements added to the list conform to the declaration type of the list
3so adding anint to aList<? extends Serializable> is allowed

Flow typing has been introduced to reduce the difference in semantics between classic and static Groovy. In particular,consider the behavior of this code in Java:

public Integer compute(String str) {    return str.length();}public String compute(Object o) {    return "Nope";}// ...Object string = "Some string";(1)Object result = compute(string);(2)System.out.println(result);(3)
1o is declared as anObject and assigned aString
2we call thecompute method witho
3and print the result

In Java, this code will outputNope, because method selection is done at compile time and based on thedeclared types.So even ifo is aString at runtime, it is still theObject version which is called, becauseo has been declaredas anObject. To be short, in Java, declared types are most important, be it variable types, parameter types or returntypes.

In Groovy, we could write:

int compute(String string) { string.length() }String compute(Object o) { "Nope" }Object o = 'string'def result = compute(o)println result

But this time, it will return6, because the method which is chosenat runtime, based on theactualargument types. So at runtime,o is aString so theString variant is used. Note that this behavior has nothingto do with type checking, it’s the way Groovy works in general: dynamic dispatch.

In type checked Groovy, we want to make sure the type checker selects the same methodat compile time, that the runtimewould choose. It is not possible in general, due to the semantics of the language, but we can make things better with flowtyping. With flow typing,o isinferred as aString when thecompute method is called, so the version which takesaString and returns anint is chosen. This means that we can infer the return type of the method to be anint, andnot aString. This is important for subsequent calls and type safety.

So in type checked Groovy, flow typing is a very important concept, which also implies that if@TypeChecked is applied,methods are selected based on theinferred types of the arguments, not on the declared types. This doesn’t ensure 100%type safety, because the type checkermay select a wrong method, but it ensures the closest semantics to dynamic Groovy.

Advanced type inference

A combination offlow typing andleast upper bound inference is used to performadvanced type inference and ensure type safety in multiple situations. In particular, program control structures arelikely to alter the inferred type of a variable:

class Top {   void methodFromTop() {}}class Bottom extends Top {   void methodFromBottom() {}}def oif (someCondition) {    o = new Top()(1)} else {    o = new Bottom()(2)}o.methodFromTop()(3)o.methodFromBottom()  // compilation error(4)
1ifsomeCondition is true,o is assigned aTop
2ifsomeCondition is false,o is assigned aBottom
3callingmethodFromTop is safe
4but callingmethodFromBottom is not, so it’s a compile time error

When the type checker visits anif/else control structure, it checks all variables which are assigned inif/else branchesand computes theleast upper bound of all assignments. This type is the type of the inferred variableafter theif/else block, so in this example,o is assigned aTop in theif branch and aBottom in theelsebranch. TheLUB of those is aTop, so after the conditional branches, the compiler inferso as beingaTop. CallingmethodFromTop will therefore be allowed, but notmethodFromBottom.

The same reasoning exists with closures and in particular closure shared variables. A closure shared variable is a variablewhich is defined outside of a closure, but used inside a closure, as in this example:

def text = 'Hello, world!'(1)def closure = {    println text(2)}
1a variable namedtext is declared
2text is used from inside a closure. It is aclosure shared variable.

Groovy allows developers to use those variables without requiring them to be final. This means that a closure sharedvariable can be reassigned inside a closure:

String resultdoSomething { String it ->    result = "Result: $it"}result = result?.toUpperCase()

The problem is that a closure is an independent block of code that can be executed (or not) atany time. In particular,doSomething may be asynchronous, for example. This means that the body of a closure doesn’t belong to the main controlflow. For that reason, the type checker also computes, for each closure shared variable, theLUB of allassignments of the variable, and will use thatLUB as the inferred type outside of the scope of the closure, like inthis example:

class Top {   void methodFromTop() {}}class Bottom extends Top {   void methodFromBottom() {}}def o = new Top()(1)Thread.start {    o = new Bottom()(2)}o.methodFromTop()(3)o.methodFromBottom()  // compilation error(4)
1a closure-shared variable is first assigned aTop
2inside the closure, it is assigned aBottom
3methodFromTop is allowed
4methodFromBottom is a compilation error

Here, it is clear that whenmethodFromBottom is called, there’s no guarantee, at compile-time or runtime that thetype ofo willeffectively be aBottom. There are chances that it will be, but we can’t make sure, because it’sasynchronous. So the type checker will only allow calls on theleast upper bound, which is here aTop.

Closures and type inference

The type checker performs special inference on closures, resulting on additional checks on one side and improved fluencyon the other side.

Return type inference

The first thing that the type checker is capable of doing is inferring thereturn type of a closure. This is simply illustrated in the following example:

@groovy.transform.TypeCheckedint testClosureReturnTypeInference(String arg) {    def cl = { "Arg: $arg" }(1)    def val = cl()(2)    val.length()(3)}
1a closure is defined, and it returns a string (more precisely aGString)
2we call the closure and assign the result to a variable
3the type checker inferred that the closure would return a string, so callinglength() is allowed

As you can see, unlike a method which declares its return type explicitly, there’s no need to declare the return typeof a closure: its type is inferred from the body of the closure.

Closures vs methods

It’s worth noting that return type inference is only applicable to closures. While the type checker could do thesame on a method, it is in practice not desirable:in general, methods can be overridden and it is not staticallypossible to make sure that the method which is called is not an overridden version. So flow typing would actuallythink that a method returns something, while in reality, it could return something else, like illustrated in thefollowing example:

@TypeCheckedclass A {    def compute() { 'some string' }(1)    def computeFully() {        compute().toUpperCase()(2)    }}@TypeCheckedclass B extends A {    def compute() { 123 }(3)}
1classA defines a methodcompute which effectively returns aString
2this will fail compilation because the return type ofcompute isdef(akaObject)
3classB extendsA and redefinescompute, this type returning anint

As you can see, if the type checker relied on the inferred return type of a method, withflow typing,the type checker could determine that it is ok to calltoUpperCase. It is in fact anerror, because a subclass canoverridecompute and return a different object. Here,B#compute returns anint, so someone callingcomputeFullyon an instance ofB would see a runtime error. The compiler prevents this from happening by using the declared returntype of methods instead of the inferred return type.

For consistency, this behavior is the same forevery method, even if they are static or final.

Parameter type inference

In addition to the return type, it is possible for a closure to infer its parameter types from the context. There aretwo ways for the compiler to infer the parameter types:

  • throughimplicit SAM type coercion

  • through API metadata

To illustrate this, lets start with an example that will fail compilation due to the inability for the type checkerto infer the parameter types:

class Person {    String name    int age}void inviteIf(Person p, Closure<Boolean> predicate) {(1)    if (predicate.call(p)) {        // send invite        // ...    }}@groovy.transform.TypeCheckedvoid failCompilation() {    Person p = new Person(name: 'Gerard', age: 55)    inviteIf(p) {(2)        it.age >= 18 // No such property: age(3)    }}
1theinviteIf method accepts aPerson and aClosure
2we call it with aPerson and aClosure
3yetit is not statically known as being aPerson and compilation fails

In this example, the closure body containsit.age. With dynamic, not type checked code, this would work, because thetype ofit would be aPerson at runtime. Unfortunately, at compile-time, there’s no way to know what is the typeofit, just by reading the signature ofinviteIf.

Explicit closure parameters

To be short, the type checker doesn’t have enough contextual information on theinviteIf method to determine staticallythe type ofit. This means that the method call needs to be rewritten like this:

inviteIf(p) { Person it ->(1)    it.age >= 18}
1the type ofit needs to be declared explicitly

By explicitly declaring the type of theit variable, you can work around the problem and make this code staticallychecked.

Parameters inferred from single-abstract method types

For an API or framework designer, there are two ways to make this more elegant for users, so that they don’t have todeclare an explicit type for the closure parameters. The first one, and easiest, is to replace the closure with aSAM type:

interface Predicate<On> { boolean apply(On e) }(1)void inviteIf(Person p, Predicate<Person> predicate) {(2)    if (predicate.apply(p)) {        // send invite        // ...    }}@groovy.transform.TypeCheckedvoid passesCompilation() {    Person p = new Person(name: 'Gerard', age: 55)    inviteIf(p) {(3)        it.age >= 18(4)    }}
1declare aSAM interface with anapply method
2inviteIf now uses aPredicate<Person> instead of aClosure<Boolean>
3there’s no need to declare the type of theit variable anymore
4it.age compiles properly, the type ofit is inferred from thePredicate#apply method signature
By using this technique, we leverage theautomatic coercion of closures to SAM types feature of Groovy.Whether you should use aSAM type or aClosure really depends on what you need to do. In a lot of cases,using a SAM interface is enough, especially if you consider functional interfaces as they are found in Java 8. However,closures provide features that are not accessible to functional interfaces. In particular, closures can have a delegate,and owner and can be manipulated as objects (for example, cloned, serialized, curried, …​) before being called. They canalso support multiple signatures (polymorphism). So if you need that kind of manipulation, it is preferable to switch tothe most advanced type inference annotations which are described below.

The original issue that needs to be solved when it comes to closure parameter type inference, that is to say, staticallydetermining the types of the arguments of a closurewithout having to have them explicitly declared, is that the Groovytype system inherits the Java type system, which is insufficient to describe the types of the arguments.

The@ClosureParams annotation

Groovy provides an annotation,@ClosureParams which is aimed at completing type information. This annotation is primarilyaimed at framework and API developers who want to extend the capabilities of the type checker by providing type inferencemetadata. This is important if your library makes use of closures and that you want the maximum level of tooling supporttoo.

Let’s illustrate this by fixing the original example, introducing the@ClosureParams annotation:

import groovy.transform.stc.ClosureParamsimport groovy.transform.stc.FirstParamvoid inviteIf(Person p, @ClosureParams(FirstParam) Closure<Boolean> predicate) {(1)    if (predicate.call(p)) {        // send invite        // ...    }}inviteIf(p) {(2)    it.age >= 18}
1the closure parameter is annotated with@ClosureParams
2it’s not necessary to use an explicit type forit, which is inferred

The@ClosureParams annotation minimally accepts one argument, which is named atype hint. A type hint is a class whichis responsible for completing type information at compile time for the closure. In this example, the type hint being usedisgroovy.transform.stc.FirstParam which indicated to the type checker that the closure will accept one parameterwhose type is the type of the first parameter of the method. In this case, the first parameter of the method isPerson,so it indicates to the type checker that the first parameter of the closure is in fact aPerson.

A second optional argument is namedoptions. Its semantics depend on thetype hint class. Groovy comes withvarious bundled type hints, illustrated in the table below:

Table 8. Predefined type hints
Type hintPolymorphic?Description and examples

FirstParam
SecondParam
ThirdParam

No

The first (resp. second, third) parameter type of the method

import groovy.transform.stc.FirstParamvoid doSomething(String str, @ClosureParams(FirstParam) Closure c) {    c(str)}doSomething('foo') { println it.toUpperCase() }
import groovy.transform.stc.SecondParamvoid withHash(String str, int seed, @ClosureParams(SecondParam) Closure c) {    c(31*str.hashCode()+seed)}withHash('foo', (int)System.currentTimeMillis()) {    int mod = it%2}
import groovy.transform.stc.ThirdParamString format(String prefix, String postfix, String o, @ClosureParams(ThirdParam) Closure c) {    "$prefix${c(o)}$postfix"}assert format('foo', 'bar', 'baz') {    it.toUpperCase()} == 'fooBAZbar'

FirstParam.FirstGenericType
SecondParam.FirstGenericType
ThirdParam.FirstGenericType

No

The first generic type of the first (resp. second, third) parameter of the method

import groovy.transform.stc.FirstParampublic <T> void doSomething(List<T> strings, @ClosureParams(FirstParam.FirstGenericType) Closure c) {    strings.each {        c(it)    }}doSomething(['foo','bar']) { println it.toUpperCase() }doSomething([1,2,3]) { println(2*it) }

Variants forSecondGenericType andThirdGenericType exist for allFirstParam,SecondParam andThirdParamtype hints.

SimpleType

No

A type hint for which the type of closure parameters comes from the options string.

import groovy.transform.stc.SimpleTypepublic void doSomething(@ClosureParams(value=SimpleType,options=['java.lang.String','int']) Closure c) {    c('foo',3)}doSomething { str, len ->    assert str.length() == len}

This type hint supports asingle signature and each of the parameter is specified as a value of theoptions arrayusing a fully-qualified type name or a primitive type.

MapEntryOrKeyValue

Yes

A dedicated type hint for closures that either work on aMap.Entry single parameter, or two parameters correspondingto the key and the value.

import groovy.transform.stc.MapEntryOrKeyValuepublic <K,V> void doSomething(Map<K,V> map, @ClosureParams(MapEntryOrKeyValue) Closure c) {    // ...}doSomething([a: 'A']) { k,v ->    assert k.toUpperCase() == v.toUpperCase()}doSomething([abc: 3]) { e ->    assert e.key.length() == e.value}

This type hintrequires that the first argument is aMap type, and infers the closure parameter types from the mapactual key/value types.

FromAbstractTypeMethods

Yes

Infers closure parameter types from the abstract method of some type. A signature is inferred foreach abstract method.

import groovy.transform.stc.FromAbstractTypeMethodsabstract class Foo {    abstract void firstSignature(int x, int y)    abstract void secondSignature(String str)}void doSomething(@ClosureParams(value=FromAbstractTypeMethods, options=["Foo"]) Closure cl) {    // ...}doSomething { a, b -> a+b }doSomething { s -> s.toUpperCase() }

If there are multiple signatures like in the example above, the type checker willonly be able to infer the types ofthe arguments if the arity of each method is different. In the example above,firstSignature takes 2 arguments andsecondSignature takes 1 argument, so the type checker can infer the argument types based on the number of arguments.But see the optional resolver class attribute discussed next.

FromString

Yes

Infers the closure parameter types from theoptions argument. Theoptions argument consists of an array of comma-separatednon-primitive types. Each element of the array corresponds to a single signature, and each comma in an element separateparameters of the signature. In short, this is the most generic type hint, and each string of theoptions map isparsedas if it was a signature literal. While being very powerful, this type hint must be avoided if you can because it increasesthe compilation times due to the necessity of parsing the type signatures.

A single signature for a closure accepting aString:

import groovy.transform.stc.FromStringvoid doSomething(@ClosureParams(value=FromString, options=["String","String,Integer"]) Closure cl) {    // ...}doSomething { s -> s.toUpperCase() }doSomething { s,i -> s.toUpperCase()*i }

A polymorphic closure, accepting either aString or aString, Integer:

import groovy.transform.stc.FromStringvoid doSomething(@ClosureParams(value=FromString, options=["String","String,Integer"]) Closure cl) {    // ...}doSomething { s -> s.toUpperCase() }doSomething { s,i -> s.toUpperCase()*i }

A polymorphic closure, accepting either aT or a pairT,T:

import groovy.transform.stc.FromStringpublic <T> void doSomething(T e, @ClosureParams(value=FromString, options=["T","T,T"]) Closure cl) {    // ...}doSomething('foo') { s -> s.toUpperCase() }doSomething('foo') { s1,s2 -> assert s1.toUpperCase() == s2.toUpperCase() }
Even though you useFirstParam,SecondParam orThirdParam as a type hint, it doesn’t strictly mean that theargument which will be passed to the closurewill be the first (resp. second, third) argument of the method call. Itonly means that thetype of the parameter of the closure will be thesame as the type of the first (resp. second, third) argument of the method call.

In short, the lack of the@ClosureParams annotation on a method accepting aClosure willnot fail compilation. If present (and it can be present in Java sources as well as Groovy sources), then the type checker hasmore information and can perform additional type inference. This makes this feature particularly interesting for framework developers.

A third optional argument is namedconflictResolutionStrategy. It can reference a class (extending fromClosureSignatureConflictResolver) that can perform additional resolution of parameter types if more thanone are found after initial inference calculations are complete. Groovy comes with a default type resolverwhich does nothing, and another which selects the first signature if multiple are found. The resolver isonly invoked if more than one signature is found and is by design a post processor. Any statements which needinjected typing information must pass one of the parameter signatures determined through type hints. Theresolver then picks among the returned candidate signatures.

@DelegatesTo

The@DelegatesTo annotation is used by the type checker to infer the type of the delegate. It allows the API designerto instruct the compiler what is the type of the delegate and the delegation strategy. The@DelegatesTo annotation isdiscussed in aspecific section.

Static compilation
Dynamic vs static

In thetype checking section, we have seen that Groovy provides optional type checking thanks to the@TypeChecked annotation. The type checker runs at compile time and performs a static analysis of dynamic code. Theprogram will behave exactly the same whether type checking has been enabled or not. This means that the@TypeCheckedannotation is neutral in regard to the semantics of a program. Even though it may be necessary to add type informationin the sources so that the program is considered type safe, in the end, the semantics of the program are the same.

While this may sound fine, there is actually one issue with this: type checking of dynamic code, done at compile time, isby definition only correct if no runtime specific behavior occurs. For example, the following program passes type checking:

class Computer {    int compute(String str) {        str.length()    }    String compute(int x) {        String.valueOf(x)    }}@groovy.transform.TypeCheckedvoid test() {    def computer = new Computer()    computer.with {        assert compute(compute('foobar')) =='6'    }}

There are twocompute methods. One accepts aString and returns anint, the other accepts anint and returnsaString. If you compile this, it is considered type safe: the innercompute('foobar') call will return anint,and callingcompute on thisint will in turn return aString.

Now, before callingtest(), consider adding the following line:

Computer.metaClass.compute = { String str -> new Date() }

Using runtime metaprogramming, we’re actually modifying the behavior of thecompute(String) method, so that instead ofreturning the length of the provided argument, it will return aDate. If you execute the program, it will fail atruntime. Since this line can be added from anywhere, in any thread, there’s absolutely no way for the type checker tostatically make sure that no such thing happens. In short, the type checker is vulnerable to monkey patching. This isjust one example, but this illustrates the concept that doing static analysis of a dynamic program is inherently wrong.

The Groovy language provides an alternative annotation to@TypeChecked which will actually make sure that the methodswhich are inferred as being calledwill effectively be called at runtime. This annotation turns the Groovy compilerinto astatic compiler, where all method calls are resolved at compile timeand the generated bytecode makes surethat this happens: the annotation is@groovy.transform.CompileStatic.

The@CompileStatic annotation

The@CompileStatic annotation can be added anywhere the@TypeChecked annotation can be used, that is to say ona class or a method. It is not necessary to add both@TypeChecked and@CompileStatic, as@CompileStatic performseverything@TypeChecked does, but in addition triggers static compilation.

Let’s take theexample which failed, but this time let’s replace the@TypeChecked annotationwith@CompileStatic:

class Computer {    int compute(String str) {        str.length()    }    String compute(int x) {        String.valueOf(x)    }}@groovy.transform.CompileStaticvoid test() {    def computer = new Computer()    computer.with {        assert compute(compute('foobar')) =='6'    }}Computer.metaClass.compute = { String str -> new Date() }test()

This is theonly difference. If we execute this program, this time, there is no runtime error. Thetest methodbecame immune to monkey patching, because thecompute methods which are called in its body are linked at compiletime, so even if the metaclass ofComputer changes, the program still behavesas expected by the type checker.

Key benefits

There are several benefits of using@CompileStatic on your code:

The performance improvements depend on the kind of program you are executing. If it is I/O bound, the difference betweenstatically compiled code and dynamic code is barely noticeable. On highly CPU intensive code, since the bytecode whichis generated is very close, if not equal, to the one that Java would produce for an equivalent program, the performanceis greatly improved.

Using theinvokedynamic version of Groovy, which is accessible to people using JDK 7 and above, the performanceof the dynamic code should be very close to the performance of statically compiled code. Sometimes, it can even be faster!There is only one way to determine which version you should choose: measuring. The reason is that depending on your programand the JVM that you use, the performance can be significantly different. In particular, theinvokedynamic version ofGroovy is very sensitive to the JVM version in use.

1.6.7. Type checking extensions

Writing a type checking extension
Towards a smarter type checker

Despite being a dynamic language, Groovy can be used with astatic type checkerat compile time, enabled using the@TypeChecked annotation. In this mode, thecompiler becomes more verbose and throws errors for, example, typos, non-existentmethods, etc. This comes with a few limitations though, most of them coming fromthe fact that Groovy remains inherently a dynamic language. For example, youwouldn’t be able to use type checking on code that uses the markup builder:

def builder = new MarkupBuilder(out)builder.html {    head {        // ...    }    body {        p 'Hello, world!'    }}

In the previous example, none of thehtml,head,body orp methodsexist. However if you execute the code, it works because Groovy uses dynamic dispatchand converts those method calls at runtime. In this builder, there’s no limitation aboutthe number of tags that you can use, nor the attributes, which means there is no chancefor a type checker to know about all the possible methods (tags) at compile time, unlessyou create a builder dedicated to HTML for example.

Groovy is a platform of choice when it comes to implement internal DSLs. The flexible syntax,combined with runtime and compile-time metaprogramming capabilities make Groovy an interestingchoice because it allows the programmer to focus on the DSL rather thanon tooling or implementation. Since Groovy DSLs are Groovy code, it’seasy to have IDE support without having to write a dedicated plugin forexample.

In a lot of cases, DSL engines are written in Groovy (or Java) then usercode is executed as scripts, meaning that you have some kind of wrapperon top of user logic. The wrapper may consist, for example, in aGroovyShell orGroovyScriptEngine that performs some tasks transparentlybefore running the script (adding imports, applying AST transforms,extending a base script,…). Often, user written scripts come toproduction without testing because the DSL logic comes to a pointwhere any user may write code using the DSL syntax. In the end, a usermay just ignore that what they write is actually code. This adds somechallenges for the DSL implementer, such as securing execution of usercode or, in this case, early reporting of errors.

For example, imagine a DSL which goal is to drive a rover on Marsremotely. Sending a message to the rover takes around 15 minutes. If therover executes the script and fails with an error (say a typo), you havetwo problems:

  • first, feedback comes only after 30 minutes (the time needed for therover to get the script and the time needed to receive the error)

  • second, some portion of the script has been executed and you may haveto change the fixed script significantly (implying that you need to knowthe current state of the rover…)

Type checking extensions is a mechanism that willallow the developer of a DSL engine to make those scripts safer byapplying the same kind of checks that static type checking allows onregular groovy classes.

The principle, here, is to fail early, that isto say fail compilation of scripts as soon as possible, and if possibleprovide feedback to the user (including nice error messages).

In short, the idea behind type checking extensions is to make the compileraware of all the runtime metaprogramming tricks that the DSL uses, so thatscripts can benefit the same level of compile-time checks as a verbose staticallycompiled code would have. We will see that you can go even further by performingchecks that a normal type checker wouldn’t do, delivering powerful compile-timechecks for your users.

The extensions attribute

The @TypeChecked annotation supports an attributenamed extensions. This parameter takes an array of stringscorresponding to a list oftype checking extensions scripts. Thosescripts are found at compile time on classpath. For example, you wouldwrite:

@TypeChecked(extensions='/path/to/myextension.groovy')void foo() { ...}

In that case, the foo methods would be type checked with the rules ofthe normal type checker completed by those found inthe myextension.groovy script. Note that while internally the typechecker supports multiple mechanisms to implement type checkingextensions (including plain old java code), the recommended way is touse those type checking extension scripts.

A DSL for type checking

The idea behind type checking extensions is to use a DSL to extend thetype checker capabilities. This DSL allows you to hook into thecompilation process, more specifically the type checking phase, using an"event-driven" API. For example, when the type checker enters a methodbody, it throws a beforeVisitMethod event that the extension can react to:

beforeVisitMethod { methodNode -> println "Entering ${methodNode.name}"}

Imagine that you have this rover DSL at hand. A user would write:

robot.move 100

If you have a class defined as such:

class Robot {    Robot move(int qt) { this }}

The script can be type checked before being executed using the followingscript:

def config = new CompilerConfiguration()config.addCompilationCustomizers(    new ASTTransformationCustomizer(TypeChecked)(1))def shell = new GroovyShell(config)(2)def robot = new Robot()shell.setVariable('robot', robot)shell.evaluate(script)(3)
1a compiler configuration adds the@TypeChecked annotation to all classes
2use the configuration in aGroovyShell
3so that scripts compiled using the shell are compiled with@TypeChecked without the user having to add it explicitly

Using the compiler configuration above, we can apply @TypeCheckedtransparently to the script. In that case, it will fail at compiletime:

[Static type checking] - The variable [robot] is undeclared.

Now, we will slightly update the configuration to include the``extensions'' parameter:

config.addCompilationCustomizers(    new ASTTransformationCustomizer(        TypeChecked,        extensions:['robotextension.groovy']))

Then add the following to your classpath:

robotextension.groovy
unresolvedVariable { var ->    if ('robot'==var.name) {        storeType(var, classNodeFor(Robot))        handled = true    }}

Here, we’re telling the compiler that if anunresolved variable is foundand that the name of the variable is robot, then we can make sure that the type of thisvariable isRobot.

Type checking extensions API
AST

The type checking API is a low level API, dealing with the AbstractSyntax Tree. You will have to know your AST well to develop extensions,even if the DSL makes it much easier than just dealing with AST codefrom plain Java or Groovy.

Events

The type checker sends the following events, to which an extensionscript can react:

Event name

setup

Called When

Called after the type checker finished initialization

Arguments

none

Usage

setup {    // this is called before anything else}

Can be used to perform setup of your extension

Event name

finish

Called When

Called after the type checker completed type checking

Arguments

none

Usage

finish {    // this is after completion    // of all type checking}

Can be used to perform additional checks after the type checker has finished its job.

Event name

unresolvedVariable

Called When

Called when the type checker finds an unresolved variable

Arguments

VariableExpression vexp

Usage

unresolvedVariable { VariableExpression vexp ->    if (vexp.name == 'people') {        storeType(vexp, LIST_TYPE)        handled = true    }}

Allows the developer to help the type checker with user-injected variables.

Event name

unresolvedProperty

Called When

Called when the type checker cannot find a property on the receiver

Arguments

PropertyExpression pexp

Usage

unresolvedProperty { PropertyExpression pexp ->    if (pexp.propertyAsString == 'longueur' &&            getType(pexp.objectExpression) == STRING_TYPE) {        storeType(pexp, int_TYPE)        handled = true    }}

Allows the developer to handle "dynamic" properties

Event name

unresolvedAttribute

Called When

Called when the type checker cannot find an attribute on the receiver

Arguments

AttributeExpression aexp

Usage

unresolvedAttribute { AttributeExpression aexp ->    if (getType(aexp.objectExpression) == STRING_TYPE) {        storeType(aexp, STRING_TYPE)        handled = true    }}

Allows the developer to handle missing attributes

Event name

beforeMethodCall

Called When

Called before the type checker starts type checking a method call

Arguments

MethodCall call

Usage

beforeMethodCall { call ->    if (isMethodCallExpression(call)            && call.methodAsString=='toUpperCase') {        addStaticTypeError('Not allowed',call)        handled = true    }}

Allows you to intercept method calls before thetype checker performs its own checks. This is useful if you want toreplace the default type checking with a custom one for a limited scope.In that case, you must set the handled flag to true, so that the typechecker skips its own checks.

Event name

afterMethodCall

Called When

Called once the type checker has finished type checking a method call

Arguments

MethodCall call

Usage

afterMethodCall { call ->    if (getTargetMethod(call).name=='toUpperCase') {        addStaticTypeError('Not allowed',call)        handled = true    }}

Allow you to perform additional checks after the typechecker has done its own checks. This is in particular useful if youwant to perform the standard type checking tests but also want to ensureadditional type safety, for example checking the arguments against eachother.Note thatafterMethodCall is called even if you didbeforeMethodCall and set the handled flag to true.

Event name

onMethodSelection

Called When

Called by the type checker when it finds a method appropriate for a method call

Arguments

Expression expr, MethodNode node

Usage

onMethodSelection { expr, node ->    if (node.declaringClass.name == 'java.lang.String') {        // calling a method on 'String'        // let’s perform additional checks!        if (++count>2) {            addStaticTypeError("You can use only 2 calls on String in your source code",expr)        }    }}

The type checker works by inferringargument types of a method call, then chooses a target method. If itfinds one that corresponds, then it triggers this event. It is forexample interesting if you want to react on a specific method call, suchas entering the scope of a method that takes a closure as argument (asin builders).Please note that this event may be thrown for various typesof expressions, not only method calls (binary expressions for example).

Event name

methodNotFound

Called When

Called by the type checker when it fails to find an appropriate method for a method call

Arguments

ClassNode receiver, String name, ArgumentListExpression argList, ClassNode[] argTypes,MethodCall call

Usage

methodNotFound { receiver, name, argList, argTypes, call ->    // receiver is the inferred type of the receiver    // name is the name of the called method    // argList is the list of arguments the method was called with    // argTypes is the array of inferred types for each argument    // call is the method call for which we couldn’t find a target method    if (receiver==classNodeFor(String)            && name=='longueur'            && argList.size()==0) {        handled = true        return newMethod('longueur', classNodeFor(String))    }}

UnlikeonMethodSelection, this event issent when the type checker cannot find a target method for a method call(instance or static). It gives you the chance to intercept the errorbefore it is sent to the user, but also set the target method.For this,you need to return a list ofMethodNode. In most situations, you wouldeither return: an empty list, meaning that you didn’t find acorresponding method, a list with exactly one element, saying that there’sno doubt about the target methodIf you return more than one MethodNode,then the compiler would throw an error to the user stating that themethod call is ambiguous, listing the possible methods.For convenience,if you want to return only one method, you are allowed to return itdirectly instead of wrapping it into a list.

Event name

beforeVisitMethod

Called When

Called by the type checker before type checking a method body

Arguments

MethodNode node

Usage

beforeVisitMethod { methodNode ->    // tell the type checker we will handle the body by ourselves    handled = methodNode.name.startsWith('skip')}

The type checker will call this method beforestarting to type check a method body. If you want, for example, toperform type checking by yourself instead of letting the type checker doit, you have to set the handled flag to true. This event can also be usedto help define the scope of your extension (for example, applying itonly if you are inside method foo).

Event name

afterVisitMethod

Called When

Called by the type checker after type checking a method body

Arguments

MethodNode node

Usage

afterVisitMethod { methodNode ->    scopeExit {        if (methods>2) {            addStaticTypeError("Method ${methodNode.name} contains more than 2 method calls", methodNode)        }    }}

Gives you the opportunity to perform additionalchecks after a method body is visited by the type checker. This isuseful if you collect information, for example, and want to performadditional checks once everything has been collected.

Event name

beforeVisitClass

Called When

Called by the type checker before type checking a class

Arguments

ClassNode node

Usage

beforeVisitClass { ClassNode classNode ->    def name = classNode.nameWithoutPackage    if (!(name[0] in 'A'..'Z')) {        addStaticTypeError("Class '${name}' doesn't start with an uppercase letter",classNode)    }}

If a class is type checked, thenbefore visiting the class, this event will be sent. It is also the casefor inner classes defined inside a class annotated with@TypeChecked. Itcan help you define the scope of your extension, or you can even totallyreplace the visit of the type checker with a custom type checkingimplementation. For that, you would have to set thehandled flag totrue

Event name

afterVisitClass

Called When

Called by the type checker after having finished the visit of a type checked class

Arguments

ClassNode node

Usage

afterVisitClass { ClassNode classNode ->    def name = classNode.nameWithoutPackage    if (!(name[0] in 'A'..'Z')) {        addStaticTypeError("Class '${name}' doesn't start with an uppercase letter",classNode)    }}

Calledfor every class being type checked after the type checker finished itswork. This includes classes annotated with@TypeChecked and anyinner/anonymous class defined in the same class with is not skipped.

Event name

incompatibleAssignment

Called When

Called when the type checker thinks that an assignment is incorrect, meaning that the right-hand side of an assignment is incompatible with the left-hand side

Arguments

ClassNode lhsType, ClassNode rhsType, Expression assignment

Usage

incompatibleAssignment { lhsType, rhsType, expr ->    if (isBinaryExpression(expr) && isAssignment(expr.operation.type)) {        if (lhsType==classNodeFor(int) && rhsType==classNodeFor(Closure)) {            handled = true        }    }}

Gives thedeveloper the ability to handle incorrect assignments. This is forexample useful if a class overridessetProperty, because in that case itis possible that assigning a variable of one type to a property ofanother type is handled through that runtime mechanism. In that case, youcan help the type checker just by telling it that the assignment isvalid (usinghandled set totrue).

Event name

incompatibleReturnType

Called When

Called when the type checker thinks that a return value is incompatibe with the return type of the enclosing closure or method

Arguments

ReturnStatement statement, ClassNode valueType

Usage

incompatibleReturnType { stmt, type ->    if (type == STRING_TYPE) {        handled = true    }}

Gives the developer the ability to handle incorrect return values. This is forexample useful when the return value will undergo implicit conversion or theenclosing closure’s target type is difficult to infer properly. In that case,you can help the type checker just by telling it that the assignment is valid(by setting thehandled property).

Event name

ambiguousMethods

Called When

Called when the type checker cannot choose between several candidate methods

Arguments

List<MethodNode> methods,  Expression origin

Usage

ambiguousMethods { methods, origin ->    // choose the method which has an Integer as parameter type    methods.find { it.parameters.any { it.type == classNodeFor(Integer) } }}

Gives thedeveloper the ability to handle incorrect assignments. This is forexample useful if a class overridessetProperty, because in that case itis possible that assigning a variable of one type to a property ofanother type is handled through that runtime mechanism. In that case, youcan help the type checker just by telling it that the assignment isvalid (usinghandled set totrue).

Of course, an extension script may consist of several blocks, and youcan have multiple blocks responding to the same event. This makes theDSL look nicer and easier to write. However, reacting to events is farfrom sufficient. If you know you can react to events, you also need todeal with the errors, which implies severalhelper methods that willmake things easier.

Working with extensions
Support classes

The DSL relies on a support class called org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport .This class itself extends org.codehaus.groovy.transform.stc.TypeCheckingExtension .Those two classes define a number ofhelper methods that will make workingwith the AST easier, especially regarding type checking. One interestingthing to know is that you have access to the type checker. This meansthat you can programmatically call methods of the type checker,including those that allow you tothrow compilation errors.

The extension script delegates to the org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport class,meaning that you have direct access to the following variables:

The type checking context contains a lot of information that is usefulin context for the type checker. For example, the current stack ofenclosing method calls, binary expressions, closures, … This informationis in particular important if you have to knowwhere you are when anerror occurs and that you want to handle it.

In addition to facilities provided byGroovyTypeCheckingExtensionSupport andStaticTypeCheckingVisitor,a type-checking DSL script imports static members fromorg.codehaus.groovy.ast.ClassHelper andorg.codehaus.groovy.transform.stc.StaticTypeCheckingSupport granting access to common types viaOBJECT_TYPE,STRING_TYPE,THROWABLE_TYPE, etc. and checks likemissesGenericsTypes(ClassNode),isClassClassNodeWrappingConcreteType(ClassNode) and so on.

Class nodes

Handling class nodes is something that needs particular attention whenyou work with a type checking extension. Compilation works with anabstract syntax tree (AST) and the tree may not be complete when you aretype checking a class. This also means that when you refer to types, youmust not use class literals such as String or HashSet, but to classnodes representing those types. This requires a certain level ofabstraction and understanding how Groovy deals with class nodes. To makethings easier, Groovy supplies several helper methods to deal with classnodes. For example, if you want to say "the type for String", you canwrite:

assert classNodeFor(String) instanceof ClassNode

You would also note that there is a variant of classNodeFor that takesa String as an argument, instead of a Class. In general, youshould not use that one, because it would create a class node forwhich the name isString, but without any method, any property, …defined on it. The first version returns a class node that is resolvedbut the second one returns one that is not. So the latter should bereserved for very special cases.

The second problem that you might encounter is referencing a type whichis not yet compiled. This may happen more often than you think. Forexample, when you compile a set of files together. In that case, if youwant to say "that variable is of type Foo" butFoo is not yetcompiled, you can still refer to theFoo class nodeusing lookupClassNodeFor:

assert lookupClassNodeFor('Foo') instanceof ClassNode
Helping the type checker

Say that you know that variable foo is of type Foo and you want totell the type checker about it. Then you can use the storeType method,which takes two arguments: the first one is the node for which you wantto store the type and the second one is the type of the node. If youlook at the implementation of storeType, you would see that itdelegates to the type checker equivalent method, which itself does a lotof work to store node metadata. You would also see that storing the typeis not limited to variables: you can set the type of any expression.

Likewise, getting the type of an AST node is just a matter ofcalling getType on that node. This would in general be what you want,but there’s something that you must understand:

  • getType returns the inferred type of an expression. This meansthat it will not return, for a variable declared of type Object theclass node for Object, but the inferred type of this variable at thispoint of the code (flow typing)

  • if you want to access the origin type of a variable (orfield/parameter), then you must call the appropriate method on the ASTnode

Throwing an error

To throw a type checking error, you only have to call theaddStaticTypeError method which takes two arguments:

  • message which is a string that will be displayed to the end user

  • anAST node responsible for the error. It’s better to provide the bestsuiting AST node because it will be used to retrieve the line and columnnumbers

isXXXExpression

It is often required to know the type of an AST node. For readability,the DSL provides a special isXXXExpression method that will delegate tox instance of XXXExpression. For example, instead of writing:

if (node instanceof BinaryExpression) {   ...}

you can just write:

if (isBinaryExpression(node)) {   ...}
Virtual methods

When you perform type checking of dynamic code, you may often face thecase when you know that a method call is valid but there is no "real"method behind it. As an example, take the Grails dynamic finders. Youcan have a method call consisting of a method named findByName(…). Asthere’s no findByName method defined in the bean, the type checkerwould complain. Yet, you would know that this method wouldn’t fail atruntime, and you can even tell what is the return type of this method.For this case, the DSL supports two special constructs that consist ofphantom methods. This means that you will return a method node thatdoesn’t really exist but is defined in the context of type checking.Three methods exist:

  • newMethod(String name, Class returnType)

  • newMethod(String name, ClassNode returnType)

  • newMethod(String name, Callable<ClassNode> return Type)

All three variants do the same: they create a new method node which nameis the supplied name and define the return type of this method.Moreover, the type checker would add those methods inthe generatedMethods list (see isGenerated below). The reason why weonly set a name and a return type is that it is only what you need in90% of the cases. For example, in the findByName example upper, theonly thing you need to know is that findByName wouldn’t fail atruntime, and that it returns a domain class. The Callable version ofreturn type is interesting because it defers the computation of thereturn type when the type checker actually needs it. This is interestingbecause in some circumstances, you may not know the actual return typewhen the type checker demands it, so you can use a closure that will becalled each time getReturnType is called by the type checker on thismethod node. If you combine this with deferred checks, you can achievepretty complex type checking including handling of forward references.

newMethod(name) {    // each time getReturnType on this method node will be called, this closure will be called!    println 'Type checker called me!'    lookupClassNodeFor(Foo) // return type}

Should you need more than the name and return type, you can alwayscreate a new MethodNode by yourself.

Scoping

Scoping is very important in DSL type checking and is one of the reasonswhy we couldn’t use a pointcut based approach to DSL type checking.Basically, you must be able to define very precisely when your extensionapplies and when it does not. Moreover, you must be able to handlesituations that a regular type checker would not be able to handle, suchas forward references:

point a(1,1)line a,b // b is referenced afterwards!point b(5,2)

Say for example that you want to handle a builder:

builder.foo {   bar   baz(bar)}

Your extension, then, should only be active once you’ve enteredthe foo method, and inactive outside this scope. But you could havecomplex situations like multiple builders in the same file or embeddedbuilders (builders in builders). While you should not try to fix allthis from start (you must accept limitations to type checking), the typechecker does offer a nice mechanism to handle this: a scoping stack,using the newScope and scopeExit methods.

  • newScope creates a new scope and puts it on top of the stack

  • scopeExits pops a scope from the stack

A scope consists of:

  • a parent scope

  • a map of custom data

If you want to look at the implementation, it’s simply aLinkedHashMap(org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport.TypeCheckingScope),but it’s quite powerful. For example, you can use such a scope to storea list of closures to be executed when you exit the scope. This is howyou would handle forward references: 

def scope = newScope()scope.secondPassChecks = []//...scope.secondPassChecks << { println 'executed later' }// ...scopeExit {    secondPassChecks*.run() // execute deferred checks}

That is to say, that if at some point you are not able to determine thetype of an expression, or that you are not able to check at this pointthat an assignment is valid or not, you can still make the check later…This is a very powerful feature. Now, newScope and scopeExitprovide some interesting syntactic sugar:

newScope {    secondPassChecks = []}

At anytime in the DSL, you can access the current scopeusing getCurrentScope() or more simply currentScope:

//...currentScope.secondPassChecks << { println 'executed later' }// ...

The general schema would then be:

  • determine apointcut where you push a new scope on stack andinitialize custom variables within this scope

  • using the various events, you can use the information stored in yourcustom scope to perform checks, defer checks,…

  • determine apointcut where you exit the scope, call scopeExitand eventually perform additional checks

Other useful methods

For the complete list of helper methods, please refer tothe org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport and org.codehaus.groovy.transform.stc.TypeCheckingExtension classes. However,take special attention to those methods:

  • isDynamic: takes a VariableExpression as argument and returns trueif the variable is a DynamicExpression, which means, in a script, thatit wasn’t defined using a type ordef.

  • isGenerated: takes a MethodNode as an argument and tells if themethod is one that was generated by the type checker extension usingthe newMethod method

  • isAnnotatedBy: takes an AST node and a Class (or ClassNode), andtells if the node is annotated with this class. For example:isAnnotatedBy(node, NotNull)

  • getTargetMethod: takes a method call as argument and returnsthe MethodNode that the type checker has determined for it

  • delegatesTo: emulates the behaviour of the @DelegatesToannotation. It allows you to tell that the argument will delegate to aspecific type (you can also specify the delegation strategy)

Advanced type checking extensions
Precompiled type checking extensions

All the examples above use type checking scripts. They are found in source form in classpath, meaning that:

  • a Groovy source file, corresponding to the type checking extension, is available on compilation classpath

  • this file is compiled by the Groovy compiler for each source unit being compiled (often, a source unit correspondsto a single file)

It is a very convenient way to develop type checking extensions, however it implies a slower compilation phase, becauseof the compilation of the extension itself for each file being compiled. For those reasons, it can be practical to relyon a precompiled extension. You have two options to do this:

  • write the extension in Groovy, compile it, then use a reference to the extension class instead of the source

  • write the extension in Java, compile it, then use a reference to the extension class

Writing a type checking extension in Groovy is the easiest path. Basically, the idea is that the type checking extensionscript becomes the body of the main method of a type checking extension class, as illustrated here:

import org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupportclass PrecompiledExtension extends GroovyTypeCheckingExtensionSupport.TypeCheckingDSL {(1)    @Override    Object run() {(2)        unresolvedVariable { var ->            if ('robot'==var.name) {                storeType(var, classNodeFor(Robot))(3)                handled = true            }        }    }}
1extending theTypeCheckingDSL class is the easiest
2then the extension code needs to go inside therun method
3and you can use the very same events as an extension written in source form

Setting up the extension is very similar to using a source form extension:

config.addCompilationCustomizers(    new ASTTransformationCustomizer(        TypeChecked,        extensions:['typing.PrecompiledExtension']))

The difference is that instead of using a path in classpath, you just specify the fully qualified class name of theprecompiled extension.

In case you really want to write an extension in Java, then you will not benefit from the type checking extension DSL.The extension above can be rewritten in Java this way:

import org.codehaus.groovy.ast.ClassHelper;import org.codehaus.groovy.ast.expr.VariableExpression;import org.codehaus.groovy.transform.stc.AbstractTypeCheckingExtension;import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor;public class PrecompiledJavaExtension extends AbstractTypeCheckingExtension {(1)    public PrecompiledJavaExtension(final StaticTypeCheckingVisitor typeCheckingVisitor) {        super(typeCheckingVisitor);    }    @Override    public boolean handleUnresolvedVariableExpression(final VariableExpression vexp) {(2)        if ("robot".equals(vexp.getName())) {            storeType(vexp, ClassHelper.make(Robot.class));            setHandled(true);            return true;        }        return false;    }}
1extend theAbstractTypeCheckingExtension class
2then override thehandleXXX methods as required
Using @Grab in a type checking extension

It is totally possible to use the@Grab annotation in a type checking extension.This means you can include libraries that would only beavailable at compile time. In that case, you must understand that youwould increase the time of compilation significantly (at least, thefirst time it grabs the dependencies).

Sharing or packaging type checking extensions

A type checking extension is just a script that need to be on classpath. As such,you can share it as is, or bundle it in a jar file that would be added to classpath.

Global type checking extensions

While you can configure the compiler to transparently add type checking extensions to yourscript, there is currently no way to apply an extension transparently just by having it onclasspath.

Type checking extensions and @CompileStatic

Type checking extensions are used with@TypeChecked but can also be used with@CompileStatic. However, you mustbe aware that:

  • a type checking extension used with@CompileStatic will in general not be sufficient to let the compiler know howto generate statically compilable code from "unsafe" code

  • it is possible to use a type checking extension with@CompileStatic just to enhance type checking, that is to sayintroducemore compilation errors, without actually dealing with dynamic code

Let’s explain the first point, which is that even if you use an extension, the compiler will not know how to compileyour code statically: technically, even if you tell the type checker what is the type of a dynamicvariable, for example, it would not know how to compile it. Is itgetBinding('foo'),getProperty('foo'),delegate.getFoo(),…? There’s absolutely no direct way to tell the static compiler how to compile suchcode even if you use a type checking extension (that would, again, only give hints about the type).

One possible solution for this particular example is to instruct the compiler to usemixed mode compilation.The more advanced one is to useAST transformations during type checking but it is far morecomplex.

Type checking extensions allow you to help the type checker where itfails, but it also allows you to fail where it doesn’t. In that context,it makes sense to support extensions for @CompileStatic too. Imaginean extension that is capable of type checking SQL queries. In that case,the extension would be valid in both dynamic and static context, becausewithout the extension, the code would still pass.

Mixed mode compilation

In the previous section, we highlighted the fact that you can activate type checking extensions with@CompileStatic. In that context, the type checker would not complain anymore about some unresolved variables orunknown method calls, but it would still wouldn’t know how to compile them statically.

Mixed mode compilation offers a third way, which is to instruct the compiler that whenever an unresolved variableor method call is found, then it should fall back to a dynamic mode. This is possible thanks to type checking extensionsand a specialmakeDynamic call.

To illustrate this, let’s come back to theRobot example:

robot.move 100

And let’s try to activate our type checking extension using@CompileStatic instead of@TypeChecked:

def config = new CompilerConfiguration()config.addCompilationCustomizers(    new ASTTransformationCustomizer(        CompileStatic,(1)        extensions:['robotextension.groovy'])(2))def shell = new GroovyShell(config)def robot = new Robot()shell.setVariable('robot', robot)shell.evaluate(script)
1Apply@CompileStatic transparently
2Activate the type checking extension

The script will run fine because the static compiler is told about the type of therobot variable, so it is capableof making a direct call tomove. But before that, how did the compiler know how to get therobot variable? In factby default, in a type checking extension, settinghandled=true on an unresolved variable will automatically triggera dynamic resolution, so in this case you don’t have anything special to make the compiler use a mixed mode. However,let’s slightly update our example, starting from the robot script:

move 100

Here you can notice that there is no reference torobot anymore. Our extension will not help then because we will notbe able to instruct the compiler thatmove is done on aRobot instance. This example of code can be executed in atotally dynamic way thanks to the help of agroovy.util.DelegatingScript:

def config = new CompilerConfiguration()config.scriptBaseClass = 'groovy.util.DelegatingScript'(1)def shell = new GroovyShell(config)def runner = shell.parse(script)(2)runner.setDelegate(new Robot())(3)runner.run()(4)
1we configure the compiler to use aDelegatingScript as the base class
2the script source needs to be parsed and will return an instance ofDelegatingScript
3we can then callsetDelegate to use aRobot as the delegate of the script
4then execute the script.move will be directly executed on the delegate

If we want this to pass with@CompileStatic, we have to use a type checking extension, so let’s update our configuration:

config.addCompilationCustomizers(    new ASTTransformationCustomizer(        CompileStatic,(1)        extensions:['robotextension2.groovy'])(2))
1apply@CompileStatic transparently
2use an alternate type checking extension meant to recognize the call tomove

Then in the previous section we have learnt how to deal with unrecognized method calls, so we are able to write thisextension:

robotextension2.groovy
methodNotFound { receiver, name, argList, argTypes, call ->    if (isMethodCallExpression(call)(1)        && call.implicitThis(2)        && 'move'==name(3)        && argTypes.length==1(4)        && argTypes[0] == classNodeFor(int)(5)    ) {        handled = true(6)        newMethod('move', classNodeFor(Robot))(7)    }}
1if the call is a method call (not a static method call)
2that this call is made on "implicit this" (no explicitthis.)
3that the method being called ismove
4and that the call is done with a single argument
5and that argument is of typeint
6then tell the type checker that the call is valid
7and that the return type of the call isRobot

If you try to execute this code, then you could be surprised that it actually fails at runtime:

java.lang.NoSuchMethodError: java.lang.Object.move()Ltyping/Robot;

The reason is very simple: while the type checking extension is sufficient for@TypeChecked, which does not involvestatic compilation, it is not enough for@CompileStatic which requires additional information. In this case, you toldthe compiler that the method existed, but you didn’t explain to itwhat method it is in reality, and what is thereceiver of the message (the delegate).

Fixing this is very easy and just implies replacing thenewMethod call with something else:

robotextension3.groovy
methodNotFound { receiver, name, argList, argTypes, call ->    if (isMethodCallExpression(call)        && call.implicitThis        && 'move'==name        && argTypes.length==1        && argTypes[0] == classNodeFor(int)    ) {        makeDynamic(call, classNodeFor(Robot))(1)    }}
1tell the compiler that the call should be make dynamic

ThemakeDynamic call does 3 things:

  • it returns a virtual method just likenewMethod

  • automatically sets thehandled flag totrue for you

  • but also marks thecall to be done dynamically

So when the compiler will have to generate bytecode for the call tomove, since it is now marked as a dynamic call,it will fall back to the dynamic compiler and let it handle the call. And since the extension tells us that the returntype of the dynamic call is aRobot, subsequent calls will be done statically!

Some would wonder why the static compiler doesn’t do this by default without an extension. It is a design decision:

  • if the code is statically compiled, we normally want type safety and best performance

  • so if unrecognized variables/method calls are made dynamic, you loose type safety, but also all support for typos atcompile time!

In short, if you want to have mixed mode compilation, ithas to be explicit, through a type checking extension, sothat the compiler, and the designer of the DSL, are totally aware of what they are doing.

makeDynamic can be used on 3 kind of AST nodes:

  • a method node (MethodNode)

  • a variable (VariableExpression)

  • a property expression (PropertyExpression)

If that is not enough, then it means that static compilation cannot be done directly and that you have to rely on ASTtransformations.

Transforming the AST in an extension

Type checking extensions look very attractive from an AST transformation design point of view: extensions have accessto context like inferred types, which is often nice to have. And an extension has a direct access to the abstractsyntax tree. Since you have access to the AST, there is nothing in theory that preventsyou from modifying the AST. However, we do not recommend you to do so, unless you are an advanced AST transformationdesigner and well aware of the compiler internals:

  • First of all, you would explicitly break the contract of type checking, which is to annotate,and only annotate the AST. Type checking should not modify the AST tree because you wouldn’t be able toguarantee anymore that code without the @TypeChecked annotationbehaves the same without the annotation.

  • If your extension is meant to work with @CompileStatic, then you can modify the AST becausethis is indeed what @CompileStatic will eventually do. Static compilation doesn’t guarantee the same semantics atdynamic Groovy so there is effectively a difference between code compiled with @CompileStatic and code compiledwith @TypeChecked. It’s up to you to choose whatever strategy you want to update the AST, but probablyusing an AST transformation that runs before type checking is easier.

  • if you cannot rely on a transformation that kicks in before the type checker, then you must bevery careful

The type checking phase is the last phase running in the compiler before bytecode generation. All other ASTtransformations run before that and the compiler does a very good job at "fixing" incorrect AST generated before thetype checking phase. As soon as you perform a transformation during type checking, for example directly in a typechecking extension, then you have to do all this work of generating a 100% compiler compliant abstract syntax tree byyourself, which can easily become complex. That’s why we do not recommend to go that way if you are beginning withtype checking extensions and AST transformations.
Examples

Examples of real life type checking extensions are easy to find. You can download the source code for Groovy andtake a look at theTypeCheckingExtensionsTestclass which is linked tovarious extension scripts.

An example of a complex type checking extension can be found in theMarkup Template Enginesource code: this template engine relies on a type checking extension and AST transformations to transform templates intofully statically compiled code. Sources for this can be foundhere.

2. Tools

2.1. Running Groovy from the commandline

2.1.1. groovy, the Groovy command

groovy invokes the Groovy command line processor. It allows you to run inline Groovy expressions, and scripts, tests or application within groovy files.It plays a similar role tojava in the Java world but handles inline scripts and rather than invoking class files, it is normally called with scriptsand will automatically call the Groovy compiler as needed.

The easiest way to run a Groovy script, test or application is to run the following command at your shell prompt:

> groovy MyScript.groovy

The.groovy part is optional. Thegroovy command supports a number of command line switches:

Short versionLong versionDescriptionExample

-a

--autosplit <splitPattern>

split lines using splitPattern (default '\s') using implicit 'split' variable

-b

--basescript <class>

Base class name for scripts (must derive from Script)

-c

--encoding <charset>

specify the encoding of the files

-cp <path>

-classpath <path>
--classpath <path>

Specify the compilation classpath. Must be the first argument.

groovy -cp lib/dep.jar MyScript

--configscript <path>

Advanced compiler configuration script

groovy --configscript config/config.groovy src/Person.groovy

-D

--define <name=value>

define a system property

-d

--debug

debug mode will print out full stack traces

--disableopt <optlist>

disables one or all optimization elements.
optlist can be a comma separated list with the elements:
all (disables all optimizations),
int (disable any int based optimizations)

-e <script>

specify an inline command line script

groovy -e "println new Date()"

-h

--help

Displays usage information for the command line groovy command

groovy --help

-i <extension>

modify files in place; create backup if extension is given (e.g. '.bak')

-l <port>

listen on a port and process inbound lines (default: 1960)

-n

process files line by line using implicit 'line' variable

-p

process files line by line and print result (see also -n)

-v

--version

display the Groovy and JVM versions

groovy -v

-pa

--parameters

Generates metadata for reflection on method parameter names on JDK 8 and above. Defaults to false.

groovy --parameters Person.groovy

-pr

--enable-preview

Enable preview Java features (jdk12+ only).

groovy --enable-preview Person.groovy

2.2. Compiling Groovy

2.2.1. groovyc, the Groovy compiler

groovyc is the Groovy compiler command line tool. It allows you to compile Groovy sources into bytecode. It playsthe same role asjavac in the Java world. The easiest way to compile a Groovy script or class is to run the following command:

groovyc MyClass.groovy

This will produce aMyClass.class file (as well as other .class files depending on the contents of the source).groovyc supportsa number of command line switches:

Short versionLong versionDescriptionExample

-cp

-classpath, --classpath

Specify the compilation classpath. Must be the first argument.

groovyc -cp lib/dep.jar MyClass.groovy

--sourcepath

Directory where to find source files. Not used anymore. Specifying this parameter will have no effect.

--temp

Temporary directory for the compiler

--encoding

Encoding of the source files

groovyc --encoding utf-8 script.groovy

--help

Displays help for the command line groovyc tool

groovyc --help

-d

Specify where to place generated class files.

groovyc -d target Person.groovy

-v

--version

Displays the compiler version

groovyc -v

-e

--exception

Displays the stack trace in case of compilation error

groovyc -e script.groovy

-j

--jointCompilation*

Enables joint compilation

groovyc -j A.groovy B.java

-b

--basescript

Base class name for scripts (must derive from Script)

--configscript

Advanced compiler configuration script

groovyc --configscript config/config.groovy src/Person.groovy

-Jproperty=value

Properties to be passed tojavac if joint compilation is enabled

groovyc -j -Jtarget=1.6 -Jsource=1.6 A.groovy B.java

-Fflag

Flags to be passed tojavac if joint compilation is enabled

groovyc -j -Fnowarn A.groovy B.java

-pa

--parameters

Generates metadata for reflection on method parameter names. Requires Java 8+.

groovyc --parameters Person.groovy

-pr

--enable-preview

Enable preview Java features (jdk12+ only).

groovy --enable-preview Person.groovy

@argfile

Read options and source files from specified file.

groovyc @conf/args

Notes:* for a full description of joint compilation, seethe joint compilation section.

2.2.2. Ant task

See thegroovyc Ant task documentation.It allows the Groovy compiler to be invoked fromApache Ant.

2.2.3. Gant

Gant is a tool for scripting Ant tasks using Groovyinstead of XML to specify the logic. As such, it has exactly the same featuresas the Groovyc Ant task.

2.2.4. Gradle

Gradle is a build tool that allows you to leverage theflexibility ofAnt, while keeping the simplicity ofconvention over configuration that tools likeMavenoffer. Builds are specified using a Groovy DSL, which offers great flexibilityand succinctness.

2.2.5. Maven integration

There are several approaches to compiling Groovy code in your Mavenprojects.GMavenPlus is themost flexible and feature rich, but like most Groovy compiler tools, it canhave difficulties with joint Java-Groovy projects (for the same reasonGMaven andGradle can have issues).TheGroovy-Eclipse compiler plugin for Mavensidesteps the joint compilation issues. Readthisfor a deeper discussion of the benefits and disadvantages of the twoapproaches.

A third approach is to use Maven’s Ant plugin to compile a groovyproject. Note that the Ant plugin is bound to the compile andtest-compile phases of the build in the example below. It will beinvoked during these phases and the contained tasks will be carried outwhich runs the Groovy compiler over the source and test directories. Theresulting Java classes will coexist with and be treated like anystandard Java classes compiled from Java source and will appear nodifferent to the JRE, or the JUnit runtime.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>com.mycomp.MyGroovy</groupId>    <artifactId>MyGroovy</artifactId>    <packaging>jar</packaging>    <version>1.0-SNAPSHOT</version>    <name>Maven Example building a Groovy project</name>    <dependencies>        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>3.8.1</version>            <scope>test</scope>        </dependency>        <dependency>            <groupId>org.codehaus.groovy</groupId>            <artifactId>groovy-all</artifactId>            <version>2.5.0</version>            <type>pom</type> <!-- required JUST since Groovy 2.5.0 -->        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <artifactId>maven-antrun-plugin</artifactId>                <executions>                    <execution>                        <id>compile</id>                        <phase>compile</phase>                        <configuration>                            <tasks>                                <mkdir dir="${basedir}/src/main/groovy"/>                                <taskdef name="groovyc"                                    classname="org.codehaus.groovy.ant.Groovyc">                                    <classpath refid="maven.compile.classpath"/>                                </taskdef>                                <mkdir dir="${project.build.outputDirectory}"/>                                <groovyc destdir="${project.build.outputDirectory}"                                    srcdir="${basedir}/src/main/groovy/" listfiles="true">                                    <classpath refid="maven.compile.classpath"/>                                </groovyc>                            </tasks>                        </configuration>                        <goals>                            <goal>run</goal>                        </goals>                    </execution>                    <execution>                        <id>test-compile</id>                        <phase>test-compile</phase>                        <configuration>                            <tasks>                                <mkdir dir="${basedir}/src/test/groovy"/>                                <taskdef name="groovyc"                                    classname="org.codehaus.groovy.ant.Groovyc">                                    <classpath refid="maven.test.classpath"/>                                </taskdef>                                <mkdir dir="${project.build.testOutputDirectory}"/>                                <groovyc destdir="${project.build.testOutputDirectory}"                                    srcdir="${basedir}/src/test/groovy/" listfiles="true">                                    <classpath refid="maven.test.classpath"/>                                </groovyc>                            </tasks>                        </configuration>                        <goals>                            <goal>run</goal>                        </goals>                    </execution>                </executions>            </plugin>        </plugins>    </build></project>

This assumes you have a Maven project setup withgroovy subfoldersas peers to the java src and test subfolders. You can use thejava/jararchetype to set this up then rename the java folders to groovy or keepthe java folders and just create groovy peer folders. There exists, alsoa groovy plugin which has not been tested or used in production. Afterdefining the build section as in the above example, you can invoke thetypical Maven build phases normally. For example,mvn test willexecute the test phase, compiling Groovy source and Groovy test sourceand finally executing the unit tests. If you runmvn jar it willexecute the jar phase bundling up all of your compiled productionclasses into a jar after all the unit tests pass. For more detail onMaven build phases consult the Maven2 documentation.

GMaven and GMavenPlus
GMaven

GMaven is the original Maven pluginfor Groovy, supporting both compiling and scripting Groovy.

Important:

You should be aware that GMaven isnot supported anymore and can havedifficulties withjoint compilation.GMavenPlus can be a good replacement, but if youare having problems with joint compilation, you might consider theGroovy Eclipse maven plugin.

GMavenPlus

GMavenPlus is a rewrite ofGMaven and is in active development. It supports most of thefeatures of GMaven (a couple notable exceptions beingmojo Javadoc tagsand support for older Groovy versions). Its joint compilation uses stubs (whichmeans it has the same potential issues asGMaven andGradle). The mainadvantages over its predecessor are that it supports recent Groovy versions,InvokeDynamic, Groovy on Android, GroovyDoc, and configuration scripts.

GMaven 2

Unlike the name might seem to suggest,GMaven 2is not aimed at replacingGMaven. In fact, it removes thenon-scripting features of the GMaven plugin. It has not yet had any release andappears to be inactive currently.

The Groovy Eclipse Maven plugin

Groovy-Eclipseprovides a compiler plugin for Maven. Using the compilerplugin, it is possible to compile your maven projects using theGroovy-Eclipse compiler. One feature unavailable elsewhere isstubless joint compilation.

2.2.6. Joint compilation

Joint compilation means that the Groovy compiler will parse theGroovy source files, create stubs for all of them, invoke the Javacompiler to compile the stubs along with Java sources, and then continuecompilation in the normal Groovy compiler way. This allows mixing ofJava and Groovy files without constraint.

Joint compilation can be enabled using the-j flag with the command-line compiler,or using a nested tag and all the attributes and further nested tags as requiredfor the Ant task.

It is important to know that if you don’t enable joint compilation and try to compileJava source files with the Groovy compiler, the Java source files will be compiled asif they were Groovy sources. In some situations, this might work since most of the Javasyntax is compatible with Groovy, but there are a number of places where semantics could be different.

2.2.7. Android support

It is possible to write an Android application in Groovy. However this requires a specialversion of the compiler, meaning that you cannot use the regulargroovyc tool to target Android bytecode. In particular, Groovyprovides specific JAR files for Android, which have a classifier ofgrooid. In order to makethings easier, aGradle plugin addssupport for the Groovy language in the Android Gradle toolchain.

The plugin can be applied like this:

buildscript {    repositories {        mavenCentral()    }    dependencies {        classpath 'com.android.tools.build:gradle:2.1.2'        classpath 'org.codehaus.groovy:groovy-android-gradle-plugin:1.0.0'    }}apply plugin: 'groovyx.android'

Then you will need to add a dependency on thegrooid version of the Groovy compiler:

dependencies {    compile 'org.codehaus.groovy:groovy:2.4.7:grooid'}

Note that if a Groovy jar does not provide agrooid classifier alternative, then it meansthat the jar is directly compatible with Android. In that case, you can add the dependency directlylike this:

dependencies {    compile 'org.codehaus.groovy:groovy:2.4.7:grooid'       // requires the grooid classifier    compile ('org.codehaus.groovy:groovy-json:2.4.7') {     // no grooid version available        transitive = false                                  // so do not depend on non-grooid version    }}

Note that thetransitive=false parameter forgroovy-json will let Gradle download the JSON support jarwithout adding a dependency onto the normal jar of Groovy.

Please make sure to go to theplugin homepage in order tofind the latest documentation and version.

2.3. Groovysh, the Groovy shell

2.3.1. Groovy : Groovy Shell

The Groovy Shell, aka.groovysh is a command-line application whichallows easy access to evaluate Groovy expressions, define classes andrun simple experiments.

Features
  • No need forgo command to execute buffer.

  • Rich cross-platform edit-line editing, history and completion thankstoJLine2.

  • ANSI colors (prompt, exception traces, etc).

  • Simple, yet robust, command system with online help, user aliassupport and more.

  • User profile support

Command-line Options and Arguments

The shell supports several options to control verbosity, ANSI coloringand other features.

./bin/groovysh --helpUsage: groovysh [options] [...]The Groovy Shell, aka groovysh, is a command-line application which allows easyaccess to evaluate Groovy expressions, define classes and run simpleexperiments.  -C, --color[=<FLAG>]    Enable or disable use of ANSI colors      -cp, -classpath, --classpath                          Specify where to find the class files - must be first                            argument  -d, --debug             Enable debug output  -D, --define=<name=value>                          Define a system property  -e, --evaluate=<CODE>   Evaluate the code first when starting interactive session  -h, --help              Display this help message  -pa, --parameters       Generate metadata for reflection on method parameter names                            (jdk8+ only)  -pr, --enable-preview   Enable preview Java features (jdk12+ only)  -q, --quiet             Suppress superfluous output  -T, --terminal=<TYPE>   Specify the terminal TYPE to use  -v, --verbose           Enable verbose output  -V, --version           Display the version
Evaluating Expressions
Simple Expressions
println "Hello"
Evaluation Result

When a complete expression is found, it is compiled and evaluated. Theresult of the evaluation is stored into the _ variable.

Multi-line Expressions

Multi-line/complex expressions (like closure or class definitions) maybe defined over several lines. When the shell detects that it has acomplete expression it will compile and evaluate it.

Define a Class
class Foo {    def bar() {        println "baz"    }}
Use the Class
foo = new Foo()foo.bar()
Variables

Shell variables areall untyped (i.e. nodef or other type information).

Thiswill set a shell variable:

foo = "bar"

But, this will evaluate a local variable and willnot be saved to the shell’s environment:

def foo = "bar"

This behavior can be changed by activatinginterpreter mode.

Functions

Functions can be defined in the shell, and will be saved for later use.

Defining a function is easy:

groovy:000> def hello(name) {groovy:001> println("Hello $name")groovy:002> }

And then using it is as one might expect:

hello("Jason")

Internally the shell creates a closure to encapsulate the function andthen binds the closure to a variable. So variables and functions sharethe same namespace.

Commands

The shell has a number of different commands, which provide rich accessto the shell’s environment.

Commands all have aname and ashortcut (which is something like\h). Commands may also have some predefined systemaliases. Usersmay also create their own aliases.

Recognized Commands
help

Display the list of commands (and aliases) or the help text for specific command.

The Command List

groovy:000> :helpFor information about Groovy, visit:    http://groovy-lang.orgAvailable commands:  :help      (:h ) Display this help message  ?          (:? ) Alias to: :help  :exit      (:x ) Exit the shell  :quit      (:q ) Alias to: :exit  import     (:i ) Import a class into the namespace  :display   (:d ) Display the current buffer  :clear     (:c ) Clear the buffer and reset the prompt counter  :show      (:S ) Show variables, classes or imports  :inspect   (:n ) Inspect a variable or the last result with the GUI object browser  :purge     (:p ) Purge variables, classes, imports or preferences  :edit      (:e ) Edit the current buffer  :load      (:l ) Load a file or URL into the buffer  .          (:. ) Alias to: :load  :save      (:s ) Save the current buffer to a file  :record    (:r ) Record the current session to a file  :history   (:H ) Display, manage and recall edit-line history  :alias     (:a ) Create an alias  :set       (:= ) Set (or list) preferences  :grab      (:g ) Add a dependency to the shell environment  :register  (:rc) Register a new command with the shell  :doc       (:D ) Open a browser window displaying the doc for the argumentFor help on a specific command type:    :help <command>

Help for a Command

While in the interactive shell, you can ask for help for any command toget more details about its syntax or function. Here is an example ofwhat happens when you ask for help for thehelp command:

groovy:000> :help :helpusage: :help [<command>]Display the list of commands or the help text for <command>.
exit

Exit the shell.

This is theonly way to exit the shell. Well, you can stillCTRL-C,but the shell will complain about an abnormal shutdown of the JVM.

import

Add a custom import which will be included for all shell evaluations.

This command can be given at any time to add new imports.

grab

Grab a dependency (Maven, Ivy, etc.) from Internet sources or cache,and add it to the Groovy Shell environment.

groovy:000> :grab 'com.google.guava:guava:19.0'groovy:000> import com.google.common.collect.BiMap===> com.google.common.collect.BiMap

This command can be given at any time to add new dependencies.

display

Display the contents of the current buffer.

This only displays the buffer of an incomplete expression. Once theexpression is complete, the buffer is reset. The prompt will update toshow the size of the current buffer as well.

Example

groovy:000> class Foo {groovy:001> def bargroovy:002> def baz() {groovy:003> :display 001> class Foo { 002> def bar 003> def baz() {
clear

Clears the current buffer, resetting the prompt counter to 000. Can be used to recover from compilation errors.

show

Show variables, classes or preferences or imports.

show variables

groovy:000> :show variablesVariables:  _ = true

show classes

show imports

show preferences

show all

inspect

Opens the GUI object browser to inspect a variable or the result of thelast evaluation.

purge

Purges objects from the shell.

purge variables

purge classes

purge imports

purge preferences

purge all

edit

Edit the current buffer in an external editor.

Currently only works on UNIX systems which have theEDITOR environmentvariable set, or have configured theeditor preference.

load

Load one or more files (or urls) into the buffer.

save

Saves the buffer’s contents to a file.

record

Record the current session to a file.

record start

record stop

record status

history

Display, manage and recall edit-line history.

history show

history recall

history flush

history clear

alias

Create an alias.

doc

Opens a browser with documentation for the provided class.

For example, we can get both the Javadoc and GDK enhancements doc forjava.util.List (shown running on JDK17):

groovy:000> :doc java.util.Listhttps://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/List.htmlhttps://docs.groovy-lang.org/4.0.27/html/groovy-jdk/java/util/List.html

This will print the documentation URLs found and open two windows (or tabs, depending on your browser):

  • one for the JDK documentation

  • one for the GDK documentation

By default, for Java classes, thejava.base module is assumed. You can specify an optional modulefor other cases (shown running on JDK17):

groovy:000> :doc java.scripting javax.script.ScriptContexthttps://docs.oracle.com/en/java/javase/17/docs/api/java.scripting/javax/script/ScriptContext.html

For backwards compatibility, if no module is specified when searching for Java classes, and no class is found in thejava.base module, an additional attempt is made to find documentation for the class in the JDK8 (pre-module) Javadoc:

groovy:000> :doc javax.script.ScriptContexthttps://docs.oracle.com/javase/8/docs/api/javax/script/ScriptContext.html

To get the Groovydoc forgroovy.ant.AntBuilder andgroovy.xml.XmlSlurper:

groovy:000> :doc groovy.ant.AntBuilderhttps://docs.groovy-lang.org/4.0.27/html/gapi/groovy/ant/AntBuilder.htmlgroovy:000> :doc groovy.xml.XmlSlurperhttps://docs.groovy-lang.org/4.0.27/html/gapi/groovy/xml/XmlSlurper.html

To get both the Groovydoc and GDK enhancements doc forgroovy.lang.Closure andgroovy.sql.GroovyResultSet:

groovy:000> :doc groovy.lang.Closurehttps://docs.groovy-lang.org/4.0.27/html/gapi/groovy/lang/Closure.htmlhttps://docs.groovy-lang.org/4.0.27/html/groovy-jdk/groovy/lang/Closure.htmlgroovy:000> :doc groovy.sql.GroovyResultSethttps://docs.groovy-lang.org/4.0.27/html/gapi/groovy/sql/GroovyResultSet.htmlhttps://docs.groovy-lang.org/4.0.27/html/groovy-jdk/groovy/sql/GroovyResultSet.html

Documentation is also available for the GDK enhancements to primitive arrays and arrays of arrays:

groovy:000> :doc int[]https://docs.groovy-lang.org/4.0.27/html/groovy-jdk/primitives-and-primitive-arrays/int%5B%5D.htmlgroovy:000> :doc double[][]https://docs.groovy-lang.org/4.0.27/html/groovy-jdk/primitives-and-primitive-arrays/double%5B%5D%5B%5D.html
In contexts where opening a browser may not be desirable, e.g. on a CI server,this command can be disabled by setting thegroovysh.disableDocCommand system property totrue.
set

Set or list preferences.

Preferences

Some aspects ofgroovysh behaviors can be customized by settingpreferences. Preferences are set using theset command or the:=shortcut.

Recognized Preferences
interpreterMode

Allows the use of typed variables (i.e.def or other type information):

groovy:000> def x = 3===> 3groovy:000> x===> 3

It’s especially useful for copy&pasting code from tutorials etc. into the running session.

verbosity

Set the shell’s verbosity level. Expected to be one of:

  • DEBUG

  • VERBOSE

  • INFO

  • QUIET

Default isINFO.

If this preference is set to an invalid value, then the previous settingwill be used, or if there is none, then the preference is removed andthe default is used.

colors

Set the shell’s use of colors.

Default istrue.

show-last-result

Show the last result after an execution.

Default istrue.

sanitize-stack-trace

Sanitize (trim-down/filter) stack traces.

Default istrue.

editor

Configures the editor used by theedit command.

Default is the value of the system environment variableEDITOR.

To use TextEdit, the default text editor on macOS, configure:set editor /Applications/TextEdit.app/Contents/MacOS/TextEdit

Setting a Preference
groovy:000> :set verbosity DEBUG
Listing Preferences

To list the currentset preferences (and their values):

groovy:000> :show preferences

Limitation: At the moment, there is no way to list all theknown/available preferences to be set.

User Profile Scripts and State
Profile Scripts
$HOME/.groovy/groovysh.profile

This script, if it exists, is loaded when the shell starts up.

$HOME/.groovy/groovysh.rc

This script, if it exists, is loaded when the shell enters interactivemode.

State
$HOME/.groovy/groovysh.history

Edit-line history is stored in this file.

Custom commands

Theregister command allows you to register custom commands in the shell. For example, writing the followingwill register theStats command:

groovy:000> :register Stats

where theStats class is a class extending theorg.apache.groovy.groovysh.CommandSupport class. For example:

import org.apache.groovy.groovysh.CommandSupportimport org.apache.groovy.groovysh.Groovyshclass Stats extends CommandSupport {    protected Stats(final Groovysh shell) {        super(shell, 'stats', 'T')    }    public Object execute(List args) {        println "Free memory: ${Runtime.runtime.freeMemory()}"    }}

Then the command can be called using:

groovy:000> :statsstatsFree memory: 139474880groovy:000>

Note that the command class must be found on classpath: you cannot define a new command from within the shell.

Troubleshooting

Pleasereport any problems yourun into. Please be sure to mark the JIRA issue with theGroovyshcomponent.

Platform Problems
Problems loading the JLine DLL

On Windows,JLine2 (which is used for the fancyshell input/history/completion fluff), uses atiny DLL file to tricktheevil Windows faux-shell (CMD.EXE orCOMMAND.COM) intoproviding Java with unbuffered input. In some rare cases, this mightfail to load or initialize.

One solution is to disable the frills and use the unsupported terminalinstance. You can do that on the command-line using the--terminalflag and set it to one of:

  • none

  • false

  • off

  • jline.UnsupportedTerminal

groovysh --terminal=none
Problems with Cygwin on Windows

Some people have issues when running groovysh with cygwin. If you havetroubles, the following may help:

stty -icanon min 1 -echogroovysh --terminal=unixstty icanon echo

2.3.2. GMavenPlus Maven Plugin

GMavenPlus is a Maven plugin with goalsthat support launching a Groovy Shell or Groovy Console bound to a Mavenproject.

2.3.3. Gradle Groovysh Plugin

Gradle Groovysh Plugin is a Gradle plugin that provides gradle tasks to start a Groovy Shell bound to a Gradle project.

2.4. groovyConsole, the Groovy swing console

2.4.1. Groovy : Groovy Console

The Groovy Swing Console allows a user to enter and run Groovy scripts.This page documents the features of this user interface.

2.4.2. Basics

image

  1. Groovy Console is launched viagroovyConsole orgroovyConsole.bat, both located in$GROOVY_HOME/bin

  2. The Console has an input area and an output area.

  3. You type a Groovy script in the input area.

  4. When you selectRun from theActions menu, the consolecompiles the script and runs it.

  5. Anything that would normally be printed onSystem.out is printed inthe output area.

  6. If the script returns a non-null result, that result is printed.

2.4.3. Features

Command-line Options and Arguments

The Groovy Console supports several options to control classpath and other features.

./bin/groovyConsole --helpUsage: groovyConsole [options] [filename]The Groovy Swing Console allows a user to enter and run Groovy scripts.      --configscript=PARAM   A script for tweaking the compiler configuration options      -cp, -classpath, --classpath                             Specify where to find the class files - must be first                               argument  -D, --define=<name=value>  Define a system property  -h, --help                 Display this help message  -pa, --parameters          Generate metadata for reflection on method parameter                               names (jdk8+ only)  -pr, --enable-preview      Enable preview Java features (jdk12+ only)  -V, --version              Display the version
Running Scripts

There are several shortcuts that you can use to run scripts or code snippets:

  • Ctrl+Enter andCtrl+R are both shortcut keys forRun Script.

  • If you highlight just part of the text in the input area, then Groovyruns just that text.

  • The result of a script is the value of the last expressionexecuted.

  • You can turn the System.out capture on and off by selectingCaptureSystem.out from theActions menu

Editing Files

You can open any text file, edit it, run it (as a Groovy Script) andthen save it again when you are finished.

  • SelectFile > Open (shortcut keyctrl+O) to open a file

  • SelectFile > Save (shortcut keyctrl+S) to save a file

  • SelectFile > New File (shortcut keyctrl+Q) to start again with ablank input area

History and results
  • You can pop up a gui inspector on the last (non-null) result byselectingInspect Last from theActions menu. The inspector is aconvenient way to view lists and maps.

  • The console remembers the last ten script runs. You can scroll backand forth through the history by selectingNext andPreviousfrom theEdit menu.Ctrl-N andctrl-P are convenient shortcut keys.

  • The last (non-null) result is bound to a variable named_ (anunderscore).

  • The last result (null and non-null) for every run in the history isbound into a list variable named (two underscores). The result ofthe last run is[-1], the result of the second to last run is__[-2] and so forth.

Interrupting a script

The Groovy console is a very handy tool to develop scripts. Often, you willfind yourself running a script multiple times until it works the way you wantit to. However, what if your code takes too long to finish or worse, createsan infinite loop? Interrupting script execution can be achieved by clickingtheinterrupt button on the small dialog box that pops up when a scriptis executing or through theinterrupt icon in the toolbar.

Toolbar

However, this may not be sufficient to interrupt a script: clicking the buttonwill interrupt the execution thread, but if your code doesn’t handle the interruptflag, the script is likely to keep running without you being able to effectivelystop it. To avoid that, you have to make sure that theScript > Allow interruptionmenu item is flagged. This will automatically apply an AST transformation to yourscript which will take care of checking the interrupt flag (@ThreadInterrupt).This way, you guarantee that the script can be interrupted even if you don’t explicitlyhandle interruption, at the cost of extra execution time.

And more
  • You can change the font size by selectingSmaller Font orLargerFont from theActions menu

  • The console can be run as an Applet thanks togroovy.ui.ConsoleApplet

  • Code is auto indented when you hit return

  • You can drag’n’drop a Groovy script over the text area to open a file

  • You can modify the classpath with which the script in the console isbeing run by adding a new JAR or a directory to the classpath from theScript menu

  • Error hyperlinking from the output area when a compilation error isexpected or when an exception is thrown

2.4.4. Embedding the Console

To embed a Swing console in your application, simply create the Consoleobject, load some variables, and then launch it. The console can be embedded ineither Java or Groovy code. The Java code for this is:

import groovy.ui.Console;    ...    Console console = new Console();    console.setVariable("var1", getValueOfVar1());    console.setVariable("var2", getValueOfVar2());    console.run();    ...

Once the console is launched, you can use the variable values in Groovycode.

2.4.5. Visualizing script output results

You can customize the way script output results are visualized. Let’ssee how we can customize this. For example, viewing a map result wouldshow something like this:

image

What you see here is the usual textual representation of a Map. But,what if we enabled custom visualization of certain results? The Swingconsole allows you to do just that. First of all, you have to ensurethat the visualization option is ticked:View → Visualize ScriptResults — for the record, all settings of the Groovy Console are storedand remembered thanks to the Preference API. There are a few resultvisualizations built-in: if the script returns ajava.awt.Image, ajavax.swing.Icon, or ajava.awt.Component with no parent, the object isdisplayed instead of itstoString() representation. Otherwise,everything else is still just represented as text. Now, create thefollowing Groovy script in~/.groovy/OutputTransforms.groovy:

import javax.swing.*transforms << { result ->    if (result instanceof Map) {        def table = new JTable(            result.collect{ k, v ->                [k, v?.inspect()] as Object[]            } as Object[][],            ['Key', 'Value'] as Object[])        table.preferredViewportSize = table.preferredSize        return new JScrollPane(table)    }}

The Groovy Swing console will execute that script on startup, injectinga transforms list in the binding of the script, so that you can add yourown script results representations. In our case, we transform the Mapinto a nice-looking Swing JTable. We’re now able to visualize mapsin a friendly and attractive fashion, as the screenshot below shows:

image

2.4.6. Advanced debugging: AST browser

Groovy Console can visualize the AST (Abstract Syntax Tree) representingthe currently edited script, as shown by the screenshot below. This isuseful when you want to understand how an AST transformationis working and particularly handy if you are developing your own AST transform.In the example below, we have annotated our class with the@Immutable annotationand the Groovy compiler has generated a lot of boilerplate code for us.We can see the code for the generated equals method in theSource tab.

AST Browser

We can even examine the JVM bytecode generated by the compiler.In the image below we are looking at the bytecode for the GroovyexpressionLocalDate.parse('2020/02/10', 'yyyy/MM/dd').

AST Browser

2.4.7. Advanced debugging: CST browser

Groovy Console can visualize the CST (Concrete Syntax Tree) representingthe initial parsing of the script. This is mainly useful for parsing gurus.

CST Browser

2.5. groovydoc, the Groovy & Java documentation generator

GroovyDoc is a tool responsible for generating documentation from your code. It acts like the Javadoc tool in theJava world but is capable of handling bothgroovy andjava files. The distribution comes with two ways of generatingdocumentation: fromcommand line or fromApache Ant. Other build toolslike Maven or Gradle also offer wrappers for Groovydoc.

2.5.1. The groovydoc command line tool

Thegroovydoc command line can be invoked to generate groovydocs:

groovydoc [options] [packagenames] [sourcefiles]

where options must be picked from the following table:

Short versionLong versionDescription

-author

Include @author paragraphs (currently not used)

-charset <charset>

Charset for cross-platform viewing of generated documentation

-classpath, -cp

--classpath

Specify where to find the class files - must befirst argument

-d

--destdir <dir>

Destination directory for output files

--debug

Enable debug output

-doctitle <html>

Include title for the overview page

-exclude <pkglist>

Specify a list of packages to exclude(separated by colons for all operating systems)

-fileEncoding <charset>

Charset for generated documentation files

-footer <html>

Include footer text for each page

-header <html>

Include header text for each page

-help

--help

Display help message

-nomainforscripts

Don’t include the implicit 'public static voidmain' method for scripts

-noscripts

Don’t process Groovy Scripts

-notimestamp

Don’t include timestamp within hidden comment in generated HTML

-noversionstamp

Don’t include Groovy version within hidden comment in generated HTML

-overview <file>

Read overview documentation from HTML file

-package

Show package/protected/public classes and members

-private

Show all classes and members

-protected

Show protected/public classes and members (default)

-public

Show only public classes and members

-quiet

Suppress superfluous output

-sourcepath <pathlist>

Specify where to find source files (dirsseparated by platform path separator)

-stylesheetfile <path>

File to change style of the generated documentation

-verbose

Enable verbose output

--version

Display the version

-windowtitle <text>

Browser window title for the documentation

-javaversion <version>

The version of the Java source files

Java Versions

The supported Java Versions forgroovydoc are defined by the JavaParser library’sLanguageLevel class.

2.5.2. The groovydoc Ant task

Thegroovydoc Ant task allows generating groovydocs from an Ant build.

Required taskdef

Assuming all the groovy jars you need are inmy.classpath (this will begroovy-VERSION.jar,groovy-ant-VERSION.jar,groovy-groovydoc-VERSION.jar plus any modules and transitive dependencies you might be using)you will need to declare this task at some point in the build.xml prior to the groovydoc task being invoked.

<taskdef name         = "groovydoc"         classname    = "org.codehaus.groovy.ant.Groovydoc"         classpathref = "my.classpath"/>
<groovydoc> Attributes
AttributeDescriptionRequired

destdir

Location to store the class files.

Yes

sourcepath

The sourcepath to use.

No

packagenames

Comma separated list of package files (with terminatingwildcard).

No

use

Create class and package usage pages.

No

windowtitle

Browser window title for the documentation (text).

No

doctitle

Include title for the package index(first) page (html-code).

No

header

Include header text for each page (html-code).

No

footer

Include footer text for each page (html-code).

No

overview

Read overview documentation from HTML file.

No

private

Show all classes and members (i.e. including private ones) ifset to ``true''.

No

javaversion

The version of the Java source files.

No

<groovydoc> Nested Elements
link

Create link to groovydoc/javadoc output at the given URL.

AttributeDescriptionRequired

packages

Comma separated list of package prefixes

Yes

href

Base URL of external site

Yes

Example #1 - <groovydoc> Ant task
<taskdef name           = "groovydoc"         classname      = "org.codehaus.groovy.ant.Groovydoc"         classpathref   = "path_to_groovy_all"/><groovydoc destdir      = "${docsDirectory}/gapi"           sourcepath   = "${mainSourceDirectory}"           packagenames = "**.*"           use          = "true"           windowtitle  = "${title}"           doctitle     = "${title}"           header       = "${title}"           footer       = "${docFooter}"           overview     = "src/main/overview.html"           private      = "false">        <link packages="java.,org.xml.,javax.,org.xml." href="http://docs.oracle.com/javase/8/docs/api/"/>        <link packages="org.apache.tools.ant."          href="http://docs.groovy-lang.org/docs/ant/api/"/>        <link packages="org.junit.,junit.framework."    href="http://junit.org/junit4/javadoc/latest/"/>        <link packages="groovy.,org.codehaus.groovy."   href="http://docs.groovy-lang.org/latest/html/api/"/>        <link packages="org.codehaus.gmaven."           href="http://groovy.github.io/gmaven/apidocs/"/></groovydoc>
Example #2 - Executing <groovydoc> from Groovy
def ant = new AntBuilder()ant.taskdef(name: "groovydoc", classname: "org.codehaus.groovy.ant.Groovydoc")ant.groovydoc(    destdir      : "${docsDirectory}/gapi",    sourcepath   : "${mainSourceDirectory}",    packagenames : "**.*",    use          : "true",    windowtitle  : "${title}",    doctitle     : "${title}",    header       : "${title}",    footer       : "${docFooter}",    overview     : "src/main/overview.html",    private      : "false") {        link(packages:"java.,org.xml.,javax.,org.xml.",href:"http://docs.oracle.com/javase/8/docs/api/")        link(packages:"groovy.,org.codehaus.groovy.",  href:"http://docs.groovy-lang.org/latest/html/api/")        link(packages:"org.apache.tools.ant.",         href:"http://docs.groovy-lang.org/docs/ant/api/")        link(packages:"org.junit.,junit.framework.",   href:"http://junit.org/junit4/javadoc/latest/")        link(packages:"org.codehaus.gmaven.",          href:"http://groovy.github.io/gmaven/apidocs/")}
Custom templates

Thegroovydoc Ant task supports custom templates, but it requires two steps:

  1. A custom groovydoc class

  2. A new groovydoc task definition

Custom Groovydoc class

The first step requires you to extend theGroovydoc class, like in the following example:

package org.codehaus.groovy.tools.groovydoc;import org.codehaus.groovy.ant.Groovydoc;/** * Overrides GroovyDoc's default class template - for testing purpose only. */public class CustomGroovyDoc extends Groovydoc {    @Override    protected String[] getClassTemplates() {        return new String[]{"org/codehaus/groovy/tools/groovydoc/testfiles/classDocName.html"};    }}

You can override the following methods:

  • getClassTemplates for class-level templates

  • getPackageTemplates for package-level templates

  • getDocTemplates for top-level templates

You can find the list of default templates in theorg.codehaus.groovy.tools.groovydoc.gstringTemplates.GroovyDocTemplateInfoclass.

Using the custom groovydoc task

Once you’ve written the class, using it is just a matter of redefining thegroovydoc task:

<taskdef name           = "groovydoc"         classname      = "org.codehaus.groovy.ant.CustomGroovyDoc"         classpathref   = "path_to_groovy_all"/>

Please note that template customization is provided as is. APIs are subject to change, so you must consider this as afragile feature.

2.5.3. GMavenPlus Maven Plugin

GMavenPlus is a Maven plugin with goals thatsupport GroovyDoc generation.

2.6. IDE integration

Many IDEs and text editors support the Groovy programming language.

EditorSyntax highlightingCode completionRefactoring

Groovy Eclipse Plugin

Yes

Yes

Yes

IntelliJ IDEA

Yes

Yes

Yes

Netbeans

Yes

Yes

Yes

Groovy Emacs Modes

Yes

No

No

TextMate

Yes

No

No

vim

Yes

No

No

UltraEdit

Yes

No

No

SlickEdit

Yes

No

No

EditRocket

Yes

No

No

VSCode

Yes

No

Yes

3. User Guides

3.1. Getting started

3.1.1. Download

From thedownload page,you will be able to download the distribution (binary and source),the Windows installer (a community artifact) and the documentation forGroovy.

For a quick and effortless start on Mac OSX, Linux, WSL2 or Cygwin, you can useSDKMAN!(The Software Development Kit Manager) to download and configure anyGroovy version of your choice.Basicinstructions can be found below.

Stable

You can learn more about this version in therelease notes or in thechangelog.

If you plan on using invokedynamic support,read those notes.

Snapshots

For those who want to test the very latest versions of Groovy and live on the bleeding edge, you can use oursnapshot builds.As soon as a build succeeds on our continuous integration server a snapshot is deployed to this repository.These snapshots are not official releases and are intended for integration testing by the developmentcommunity prior to official versions being released. We welcome any feedback.

Prerequisites

Groovy 4.0 requires Java 8+ with support for up to Java 16.

Various Groovy CI servers run the test suite (with more than 10000 tests) across numerous versions of Java.Those servers are also useful to look at to confirm supported Java versions for different Groovy releases.

3.1.2. Maven Repository

If you wish to embed Groovy in your application, you may just prefer to point your buildto your favourite maven repository or the Groovyartifactory instance.Please see thedownload page for available modules for each Groovy version.

3.1.3. SDKMAN! (The Software Development Kit Manager)

This tool makes installing Groovy on any Bash platform (Mac OSX, Linux, Cygwin, Solaris or FreeBSD) very easy.

Simply open a new terminal and enter:

$ curl -s "https://get.sdkman.io" | bash

Follow the instructions on-screen to complete installation.

Open a new terminal or type the command:

$ source "$HOME/.sdkman/bin/sdkman-init.sh"

Then install the latest stable Groovy:

$ sdk install groovy

After installation is complete and you’ve made it your default version, test it with:

$ groovy -version

That’s all there is to it!

3.1.4. Other ways to get Groovy

Installation on Mac OS X
MacPorts

If you’re on macOS and haveMacPorts installed, you can run:

sudo port install groovy
Homebrew

If you’re on macOS and haveHomebrew installed, you can run:

brew install groovy
Installation on Windows

If you’re on Windows, you can also use theWindows installer.

Other Distributions

You may download other distributions of Groovy from the ASFarchive repositoryor from the Groovyartifactory instance (also includes pre-ASF versions).

Source Code

If you prefer to live on the bleeding edge, you can also grab thesource code from GitHub.

IDE plugin

If you are an IDE user, you can just grab the latestIDE plugin and follow the plugin installation instructions.

3.1.5. Install Binary

These instructions describe how to install a binary distribution ofGroovy:

  • Download a binary distribution of Groovy and unpack it into some folder on your local file system.

  • Set yourGROOVY_HOME environment variable to the directory where you unpacked the distribution.

  • AddGROOVY_HOME/bin to yourPATH environment variable.

  • Set yourJAVA_HOME environment variable to point to your JDK. On OS X this is/Library/Java/Home, on other unixes its often/usr/java etc. If you’ve already installed tools like Ant or Maven you’ve probably already done this step.

You should now have Groovy installed properly. You can test this by typing the following in a command shell:

groovysh

Which should create an interactive groovy shell where you can type Groovy statements. Or to run theSwing interactive console type:

groovyConsole

To run a specific Groovy script type:

groovy SomeScript

3.2. Differences with Java

Groovy tries to be as natural as possible for Java developers. We’vetried to follow the principle of least surprise when designing Groovy,particularly for developers learning Groovy who’ve come from a Javabackground.

Here we list all the major differences between Java and Groovy.

3.2.1. Default imports

All these packages and classes are imported by default, i.e. you do nothave to use an explicitimport statement to use them:

  • java.io.*

  • java.lang.*

  • java.math.BigDecimal

  • java.math.BigInteger

  • java.net.*

  • java.util.*

  • groovy.lang.*

  • groovy.util.*

3.2.2. Multi-methods

In Groovy, the methods which will be invoked are chosen at runtime. This is called runtime dispatch or multi-methods. Itmeans that the method will be chosen based on the types of the arguments at runtime. In Java, this is the opposite: methodsare chosen at compile time, based on the declared types.

The following code, written as Java code, can be compiled in both Java and Groovy, but it will behave differently:

int method(String arg) {    return 1;}int method(Object arg) {    return 2;}Object o = "Object";int result = method(o);

In Java, you would have:

assertEquals(2, result);

Whereas in Groovy:

assertEquals(1, result);

That is because Java will use the static information type, which is thato is declared as anObject, whereasGroovy will choose at runtime, when the method is actually called. Since it is called with aString, then theString version is called.

3.2.3. Array initializers

In Java, array initializers take either of these two forms:

int[] array = {1, 2, 3};             // Java array initializer shorthand syntaxint[] array2 = new int[] {4, 5, 6};  // Java array initializer long syntax

In Groovy, the{ …​ } block is reserved for closures.That means that you cannot create array literals using Java’s array initializer shorthand syntax.You instead borrow Groovy’s literal list notation like this:

int[] array = [1, 2, 3]

For Groovy 3+, you can optionally use the Java’s array initializer long syntax:

def array2 = new int[] {1, 2, 3} // Groovy 3.0+ supports the Java-style array initialization long syntax

3.2.4. Package scope visibility

In Groovy, omitting a modifier on a field doesn’t result in a package-private field like in Java:

class Person {    String name}

Instead, it is used to create aproperty, that is to say aprivate field, an associatedgetter and an associatedsetter.

It is possible to create a package-private field by annotating it with@PackageScope:

class Person {    @PackageScope String name}

3.2.5. ARM blocks

Java 7 introduced ARM (Automatic Resource Management) blocks (also know as try-with-resources) blocks like this:

Path file = Paths.get("/path/to/file");Charset charset = Charset.forName("UTF-8");try (BufferedReader reader = Files.newBufferedReader(file, charset)) {    String line;    while ((line = reader.readLine()) != null) {        System.out.println(line);    }} catch (IOException e) {    e.printStackTrace();}

Such blocks are supported from Groovy 3+.However, Groovy provides various methods relying on closures, which have the same effect while being more idiomatic. For example:

new File('/path/to/file').eachLine('UTF-8') {   println it}

or, if you want a version closer to Java:

new File('/path/to/file').withReader('UTF-8') { reader ->   reader.eachLine {       println it   }}

3.2.6. Inner classes

The implementation of anonymous inner classes and nested classes follow Java closely,but there are some differences, e.g.local variables accessed from within such classes don’t have to be final.We piggyback on some implementation details we use forgroovy.lang.Closurewhen generating inner class bytecode.
Static inner classes

Here’s an example of static inner class:

class A {    static class B {}}new A.B()

The usage of static inner classes is the best supported one. If youabsolutely need an inner class, you should make it a static one.

Anonymous Inner Classes
import java.util.concurrent.CountDownLatchimport java.util.concurrent.TimeUnitCountDownLatch called = new CountDownLatch(1)Timer timer = new Timer()timer.schedule(new TimerTask() {    void run() {        called.countDown()    }}, 0)assert called.await(10, TimeUnit.SECONDS)
Creating Instances of Non-Static Inner Classes

In Java you can do this:

public class Y {    public class X {}    public X foo() {        return new X();    }    public static X createX(Y y) {        return y.new X();    }}

Before 3.0.0, Groovy doesn’t support they.new X() syntax. Instead, you have to writenew X(y), like in the code below:

public class Y {    public class X {}    public X foo() {        return new X()    }    public static X createX(Y y) {        return new X(y)    }}
Caution though, Groovy supports calling methods with oneparameter without giving an argument. The parameter will then have thevalue null. Basically the same rules apply to calling a constructor.There is a danger that you will write new X() instead of new X(this) forexample. Since this might also be the regular way we have not yet founda good way to prevent this problem.
Groovy 3.0.0 supports Java style syntax for creating instances of non-static inner classes.

3.2.7. Lambda expressions and the method reference operator

Java 8+ supports lambda expressions and the method reference operator (::):

Runnable run = () -> System.out.println("Run");  // Javalist.forEach(System.out::println);

Groovy 3 and above also support these within the Parrot parser.In earlier versions of Groovy you should use closures instead:

Runnable run = { println 'run' }list.each { println it } // or list.each(this.&println)

3.2.8. GStrings

As double-quoted string literals are interpreted asGString values, Groovy may failwith compile error or produce subtly different code if a class withString literalcontaining a dollar character is compiled with Groovy and Java compiler.

While typically, Groovy will auto-cast betweenGString andString if an API declaresthe type of a parameter, beware of Java APIs that accept anObject parameter and thencheck the actual type.

3.2.9. String and Character literals

Singly-quoted literals in Groovy are used forString, and double-quoted result inString orGString, depending whether there is interpolation in the literal.

assert 'c'.class == Stringassert "c".class == Stringassert "c${1}".class in GString

Groovy will automatically cast a single-characterString tochar only when assigning toa variable of typechar. When calling methods with arguments of typechar we needto either cast explicitly or make sure the value has been cast in advance.

char a = 'a'assert Character.digit(a, 16) == 10: 'But Groovy does boxing'assert Character.digit((char) 'a', 16) == 10try {  assert Character.digit('a', 16) == 10  assert false: 'Need explicit cast'} catch(MissingMethodException e) {}

Groovy supports two styles of casting and in the case of casting tochar thereare subtle differences when casting a multi-char strings. The Groovy style cast ismore lenient and will take the first character, while the C-style cast will failwith exception.

// for single char strings, both are the sameassert ((char) "c").class == Characterassert ("c" as char).class == Character// for multi char strings they are nottry {  ((char) 'cx') == 'c'  assert false: 'will fail - not castable'} catch(GroovyCastException e) {}assert ('cx' as char) == 'c'assert 'cx'.asType(char) == 'c'

3.2.10. Behaviour of==

In Java,== means equality of primitive types or identity for objects.In Groovy,== means equality in all places.For non-primitives, it translates toa.compareTo(b) == 0,when evaluating equality forComparable objects,anda.equals(b) otherwise.

To check for identity (reference equality), use theis method:a.is(b).From Groovy 3, you can also use the=== operator (or negated version):a === b (orc !== d).

3.2.11. Primitives and wrappers

In a pure object-oriented language, everything would be an object.Java takes the stance that primitive types, such as int, boolean and double,are used very frequently and worthy of special treatment.Primitives can be efficiently stored and manipulated but can’t be used in all contexts where an object could be used.Luckily, Java auto boxes and unboxes primitives when they are passed as parameters or used as return types:

public class Main {           // Java   float f1 = 1.0f;   Float f2 = 2.0f;   float add(Float a1, float a2) { return a1 + a2; }   Float calc() { return add(f1, f2); }(1)    public static void main(String[] args) {       Float calcResult = new Main().calc();       System.out.println(calcResult); // => 3.0    }}
1Theadd method expects wrapper then primitive type arguments,but we are supplying parameters with a primitive then wrapper type.Similarly, the return type fromadd is primitive, but we need the wrapper type.

Groovy does the same:

class Main {    float f1 = 1.0f    Float f2 = 2.0f    float add(Float a1, float a2) { a1 + a2 }    Float calc() { add(f1, f2) }}assert new Main().calc() == 3.0

Groovy, also supports primitives and object types, however, it goes a little furtherin pushing OO purity; it tries hard to treateverything as an object.Any primitive typed variable or field can be treated like an object, and itwill beauto-wrapped as needed.While primitive types might be used under the covers,their use should be indistinguishable from normal object use whenever possible,and they will be boxed/unboxed as needed.

Here is a little example using Java trying to (incorrectly for Java) dereference a primitivefloat:

public class Main {           // Java    public float z1 = 0.0f;    public static void main(String[] args){      new Main().z1.equals(1.0f); // DOESN'T COMPILE, error: float cannot be dereferenced    }}

The same example using Groovy compiles and runs successfully:

class Main {    float z1 = 0.0f}assert !(new Main().z1.equals(1.0f))

Because of Groovy’s additional use of un/boxing, it does not followJava’s behavior of widening taking priority over boxing.Here’s an example usingint

int im(i)void m(long l) {(1)    println "in m(long)"}void m(Integer i) {(2)    println "in m(Integer)"}
1This is the method that Java would call, since widening has precedence over unboxing.
2This is the method Groovy actually calls, since all primitive references use their wrapper class.
Numeric Primitive Optimisation with@CompileStatic

Since Groovy converts to wrapper classes in more places, you might wonderwhether it produces less efficient bytecode for numeric expressions.Groovy has a highly optimised set of classes for doing math computations.When using@CompileStatic, expressions involving only primitivesuses the same bytecode that Java would use.

Positive/Negative zero edge case

Java float/double operations for both primitives and wrapper classes follow the IEEE 754 standardbut there is an interesting edge case involving positive and negative zero.The standard supports distinguishing between these two cases and while in many scenariosprogrammers may not care about the difference, in some mathematical or data science scenariosit is important to cater for the distinction.

For primitives, Java maps down onto a specialbytecode instructionwhen comparing such values which has the property that"Positive zero and negative zero are considered equal".

jshell> float f1 = 0.0ff1 ==> 0.0jshell> float f2 = -0.0ff2 ==> -0.0jshell> f1 == f2$3 ==> true

For the wrapper classes, e.g.java.base/java.lang.Float#equals(java.lang.Object),the result isfalse for this same case.

jshell> Float f1 = 0.0ff1 ==> 0.0jshell> Float f2 = -0.0ff2 ==> -0.0jshell> f1.equals(f2)$3 ==> false

Groovy on the one hand tries to follow Java behavior closely, but on the otherswitches automatically between primitives and wrapped equivalents in more places.To avoid confusion we recommend the following guidelines:

  • If you wish to distinguish between positive and negative zero, use theequalsmethod directly or cast any primitives to their wrapper equivalent before using==.

  • If you wish to ignore the difference between positive and negative zero, use theequalsIgnoreZeroSignmethod directly or cast any non-primitives to their primitive equivalent before using==.

These guidelines are illustrated in the following example:

float f1 = 0.0ffloat f2 = -0.0fFloat f3 = 0.0fFloat f4 = -0.0fassert f1 == f2assert (Float) f1 != (Float) f2assert f3 != f4(1)assert (float) f3 == (float) f4assert !f1.equals(f2)assert !f3.equals(f4)assert f1.equalsIgnoreZeroSign(f2)assert f3.equalsIgnoreZeroSign(f4)
1Recall that for non-primitives,== maps to.equals()

3.2.12. Conversions

Java does automatic widening and narrowingconversions.

Table 9. Java Conversions

Converts to

Converts from

boolean

byte

short

char

int

long

float

double

boolean

-

N

N

N

N

N

N

N

byte

N

-

Y

C

Y

Y

Y

Y

short

N

C

-

C

Y

Y

Y

Y

char

N

C

C

-

Y

Y

Y

Y

int

N

C

C

C

-

Y

T

Y

long

N

C

C

C

C

-

T

T

float

N

C

C

C

C

C

-

Y

double

N

C

C

C

C

C

C

-

  • 'Y' indicates a conversion Java can make

  • 'C' indicates a conversion Java can make when there is an explicit cast

  • 'T` indicates a conversion Java can make but data is truncated

  • 'N' indicates a conversion Java can’t make

Groovy expands greatly on this.

Table 10. Groovy Conversions

Converts to

Converts from

boolean

Boolean

byte

Byte

short

Short

char

Character

int

Integer

long

Long

BigInteger

float

Float

double

Double

BigDecimal

boolean

-

B

N

N

N

N

N

N

N

N

N

N

N

N

N

N

N

N

Boolean

B

-

N

N

N

N

N

N

N

N

N

N

N

N

N

N

N

N

byte

T

T

-

B

Y

Y

Y

D

Y

Y

Y

Y

Y

Y

Y

Y

Y

Y

Byte

T

T

B

-

Y

Y

Y

D

Y

Y

Y

Y

Y

Y

Y

Y

Y

Y

short

T

T

D

D

-

B

Y

D

Y

Y

Y

Y

Y

Y

Y

Y

Y

Y

Short

T

T

D

T

B

-

Y

D

Y

Y

Y

Y

Y

Y

Y

Y

Y

Y

char

T

T

Y

D

Y

D

-

D

Y

D

Y

D

D

Y

D

Y

D

D

Character

T

T

D

D

D

D

D

-

D

D

D

D

D

D

D

D

D

D

int

T

T

D

D

D

D

Y

D

-

B

Y

Y

Y

Y

Y

Y

Y

Y

Integer

T

T

D

D

D

D

Y

D

B

-

Y

Y

Y

Y

Y

Y

Y

Y

long

T

T

D

D

D

D

Y

D

D

D

-

B

Y

T

T

T

T

Y

Long

T

T

D

D

D

T

Y

D

D

T

B

-

Y

T

T

T

T

Y

BigInteger

T

T

D

D

D

D

D

D

D

D

D

D

-

D

D

D

D

T

float

T

T

D

D

D

D

T

D

D

D

D

D

D

-

B

Y

Y

Y

Float

T

T

D

T

D

T

T

D

D

T

D

T

D

B

-

Y

Y

Y

double

T

T

D

D

D

D

T

D

D

D

D

D

D

D

D

-

B

Y

Double

T

T

D

T

D

T

T

D

D

T

D

T

D

D

T

B

-

Y

BigDecimal

T

T

D

D

D

D

D

D

D

D

D

D

D

T

D

T

D

-

  • 'Y' indicates a conversion Groovy can make

  • 'D' indicates a conversion Groovy can make when compiled dynamically or explicitly cast

  • 'T' indicates a conversion Groovy can make but data is truncated

  • 'B' indicates a boxing/unboxing operation

  • 'N' indicates a conversion Groovy can’t make.

The truncation usesGroovy Truth when converting toboolean/Boolean. Convertingfrom a number to a character casts theNumber.intvalue() tochar. Groovy constructsBigInteger andBigDecimalusingNumber.doubleValue() when converting from aFloat orDouble, otherwise it constructs usingtoString().Other conversions have their behavior defined byjava.lang.Number.

3.2.13. Extra keywords

Groovy has many of the same keywords as Java and Groovy 3 and above also has the samevar reserved type as Java.In addition, Groovy has the following keywords:

  • as

  • def

  • in

  • trait

  • it // within closures

Groovy is less stringent than Java in that it allows some keywords to appear in places that would be illegal in Java,e.g. the following is valid:var var = [def: 1, as: 2, in: 3, trait: 4].Never-the-less, you are discouraged from using the above keywords in places that might cause confusion even whenthe compiler might be happy. In particular, avoid using them for variable, method and class names,so our previousvar var example would be considered poor style.

Additional documentation is available forkeywords.

3.3. Groovy Development Kit

3.3.1. Working with IO

Groovy provides a number ofhelper methods for workingwith I/O. While you could use standard Java code in Groovy to deal with those,Groovy provides much more convenient ways to handle files, streams, readers, …​

In particular, you should take a look at methods added to:

The following section focuses on sample idiomatic constructs using helper methods available above but is not meantto be a complete description of all available methods. For that, please read theGDK API.

Reading files

As a first example, let’s see how you would print all lines of a text file in Groovy:

new File(baseDir, 'haiku.txt').eachLine { line ->    println line}

TheeachLine method is a method added to theFile class automatically by Groovy and has many variants, for exampleif you need to know the line number, you can use this variant:

new File(baseDir, 'haiku.txt').eachLine { line, nb ->    println "Line $nb: $line"}

If for whatever reason an exception is thrown in theeachLine body, the method makes sure that the resourceis properly closed. This is true for all I/O resource methods that Groovy adds.

For example in some cases you will prefer to use aReader, but still benefit from the automatic resource managementfrom Groovy. In the next example, the readerwill be closed even if the exception occurs:

def count = 0, MAXSIZE = 3new File(baseDir,"haiku.txt").withReader { reader ->    while (reader.readLine()) {        if (++count > MAXSIZE) {            throw new RuntimeException('Haiku should only have 3 verses')        }    }}

Should you need to collect the lines of a text file into a list, you can do:

def list = new File(baseDir, 'haiku.txt').collect {it}

Or you can even leverage theas operator to get the contents of the file into an array of lines:

def array = new File(baseDir, 'haiku.txt') as String[]

How many times did you have to get the contents of a file into abyte[] and how much code does it require? Groovymakes it very easy actually:

byte[] contents = file.bytes

Working with I/O is not limited to dealing with files. In fact, a lot of operations rely on input/output streams,hence why Groovy adds a lot of support methods to those, as you can see in thedocumentation.

As an example, you can obtain anInputStream from aFile very easily:

def is = new File(baseDir,'haiku.txt').newInputStream()// do something ...is.close()

However you can see that it requires you to deal with closing the inputstream. In Groovy it is in general a betteridea to use thewithInputStream idiom that will take care of that for you:

new File(baseDir,'haiku.txt').withInputStream { stream ->    // do something ...}
Writing files

Of course in some cases you won’t want to read but write a file. One of the options is to use aWriter:

new File(baseDir,'haiku.txt').withWriter('utf-8') { writer ->    writer.writeLine 'Into the ancient pond'    writer.writeLine 'A frog jumps'    writer.writeLine 'Water’s sound!'}

But for such a simple example, using the<< operator would have been enough:

new File(baseDir,'haiku.txt') << '''Into the ancient pondA frog jumpsWater’s sound!'''

Of course we do not always deal with text contents, so you could use theWriter or directly write bytes as inthis example:

file.bytes = [66,22,11]

Of course you can also directly deal with output streams. For example, here is how you would create an outputstream to write into a file:

def os = new File(baseDir,'data.bin').newOutputStream()// do something ...os.close()

However you can see that it requires you to deal with closing the output stream. Again it is in general a betteridea to use thewithOutputStream idiom that will handle the exceptions and close the stream in any case:

new File(baseDir,'data.bin').withOutputStream { stream ->    // do something ...}
Traversing file trees

In scripting contexts it is a common task to traverse a file tree in order to find some specific files and dosomething with them. Groovy provides multiple methods to do this. For example you can perform something on all filesof a directory:

dir.eachFile { file ->(1)    println file.name}dir.eachFileMatch(~/.*\.txt/) { file ->(2)    println file.name}
1executes the closure code on each file found in the directory
2executes the closure code on files in the directory matching the specified pattern

Often you will have to deal with a deeper hierarchy of files, in which case you can useeachFileRecurse:

dir.eachFileRecurse { file ->(1)    println file.name}dir.eachFileRecurse(FileType.FILES) { file ->(2)    println file.name}
1executes the closure code on each file or directory found in the directory, recursively
2executes the closure code only on files, but recursively

For more complex traversal techniques you can use thetraverse method, which requires you to set a special flagindicating what to do with the traversal:

dir.traverse { file ->    if (file.directory && file.name=='bin') {        FileVisitResult.TERMINATE(1)    } else {        println file.name        FileVisitResult.CONTINUE(2)    }}
1if the current file is a directory and its name isbin, stop the traversal
2otherwise print the file name and continue
Data and objects

In Java it is not uncommon to serialize and deserialize data using thejava.io.DataOutputStream andjava.io.DataInputStream classes respectively. Groovy will make it even easier to deal with them. For example, you couldserialize data into a file and deserialize it using this code:

boolean b = trueString message = 'Hello from Groovy'// Serialize data into a filefile.withDataOutputStream { out ->    out.writeBoolean(b)    out.writeUTF(message)}// ...// Then read it backfile.withDataInputStream { input ->    assert input.readBoolean() == b    assert input.readUTF() == message}

And similarly, if the data you want to serialize implements theSerializable interface, you can proceed withan object output stream, as illustrated here:

Person p = new Person(name:'Bob', age:76)// Serialize data into a filefile.withObjectOutputStream { out ->    out.writeObject(p)}// ...// Then read it backfile.withObjectInputStream { input ->    def p2 = input.readObject()    assert p2.name == p.name    assert p2.age == p.age}
Executing External Processes

The previous section described how easy it was to deal with files, readers or streams in Groovy. However in domainslike system administration or devops it is often required to communicate with external processes.

Groovy provides a simple way to execute command line processes. Simplywrite the command line as a string and call theexecute() method.E.g., on a *nix machine (or a Windows machine with appropriate *nixcommands installed), you can execute this:

def process = "ls -l".execute()(1)println "Found text ${process.text}"(2)
1executes thels command in an external process
2consume the output of the command and retrieve the text

Theexecute() method returns ajava.lang.Process instance which willsubsequently allow the in/out/err streams to be processed and the exitvalue from the process to be inspected etc.

e.g. here is the same command as above but we will now process theresulting stream a line at a time:

def process = "ls -l".execute()(1)process.in.eachLine { line ->(2)    println line(3)}
1executes thels command in an external process
2for each line of the input stream of the process
3print the line

It is worth noting thatin corresponds to an input stream to the standard output of the command.out will referto a stream where you can send data to the process (its standard input).

Remember that many commands are shell built-ins and need specialhandling. So if you want a listing of files in a directory on a Windowsmachine and write:

def process = "dir".execute()println "${process.text}"

you will receive anIOException saying Cannot run program "dir":CreateProcess error=2, The system cannot find the file specified.

This is becausedir is built-in to the Windows shell (cmd.exe) andcan’t be run as a simple executable. Instead, you will need to write:

def process = "cmd /c dir".execute()println "${process.text}"

Also, because this functionality currently makes use ofjava.lang.Process undercover, the deficiencies of that classmust be taken into consideration. In particular, the javadocfor this class says:

Because some native platforms only provide limited buffer size forstandard input and output streams, failure to promptly write the inputstream or read the output stream of the subprocess may cause thesubprocess to block, and even deadlock

Because of this, Groovy provides some additional helper methods whichmake stream handling for processes easier.

Here is how to gobble all of the output (including the error streamoutput) from your process:

def p = "rm -f foo.tmp".execute([], tmpDir)p.consumeProcessOutput()p.waitFor()

There are also variations ofconsumeProcessOutput that make use ofStringBuffer,InputStream,OutputStream etc…​ For a complete list, please read theGDK API for java.lang.Process

In addition, there is apipeTo command (mapped to|to allow overloading) which lets the output stream of one process be fedinto the input stream of another process.

Here are some examples of use:

Pipes in action
proc1 = 'ls'.execute()proc2 = 'tr -d o'.execute()proc3 = 'tr -d e'.execute()proc4 = 'tr -d i'.execute()proc1 | proc2 | proc3 | proc4proc4.waitFor()if (proc4.exitValue()) {    println proc4.err.text} else {    println proc4.text}
Consuming errors
def sout = new StringBuilder()def serr = new StringBuilder()proc2 = 'tr -d o'.execute()proc3 = 'tr -d e'.execute()proc4 = 'tr -d i'.execute()proc4.consumeProcessOutput(sout, serr)proc2 | proc3 | proc4[proc2, proc3].each { it.consumeProcessErrorStream(serr) }proc2.withWriter { writer ->    writer << 'testfile.groovy'}proc4.waitForOrKill(1000)println "Standard output: $sout"println "Standard error: $serr"

3.3.2. Working with collections

Groovy provides native support for various collection types, includinglists,maps orranges. Most of those are based on the Javacollection types and decorated with additional methods found in theGroovy development kit.

Lists
List literals

You can create lists as follows. Notice that[] is the empty listexpression.

def list = [5, 6, 7, 8]assert list.get(2) == 7assert list[2] == 7assert list instanceof java.util.Listdef emptyList = []assert emptyList.size() == 0emptyList.add(5)assert emptyList.size() == 1

Each list expression creates an implementation ofjava.util.List.

Of course lists can be used as a source to construct another list:

def list1 = ['a', 'b', 'c']//construct a new list, seeded with the same items as in list1def list2 = new ArrayList<String>(list1)assert list2 == list1 // == checks that each corresponding element is the same// clone() can also be calleddef list3 = list1.clone()assert list3 == list1

A list is an ordered collection of objects:

def list = [5, 6, 7, 8]assert list.size() == 4assert list.getClass() == ArrayList     // the specific kind of list being usedassert list[2] == 7                     // indexing starts at 0assert list.getAt(2) == 7               // equivalent method to subscript operator []assert list.get(2) == 7                 // alternative methodlist[2] = 9assert list == [5, 6, 9, 8,]           // trailing comma OKlist.putAt(2, 10)                       // equivalent method to [] when value being changedassert list == [5, 6, 10, 8]assert list.set(2, 11) == 10            // alternative method that returns old valueassert list == [5, 6, 11, 8]assert ['a', 1, 'a', 'a', 2.5, 2.5f, 2.5d, 'hello', 7g, null, 9 as byte]//objects can be of different types; duplicates allowedassert [1, 2, 3, 4, 5][-1] == 5             // use negative indices to count from the endassert [1, 2, 3, 4, 5][-2] == 4assert [1, 2, 3, 4, 5].getAt(-2) == 4       // getAt() available with negative index...try {    [1, 2, 3, 4, 5].get(-2)                 // but negative index not allowed with get()    assert false} catch (e) {    assert e instanceof IndexOutOfBoundsException}
List as a boolean expression

Lists can be evaluated as aboolean value:

assert ![]             // an empty list evaluates as false//all other lists, irrespective of contents, evaluate as trueassert [1] && ['a'] && [0] && [0.0] && [false] && [null]
Iterating on a list

Iterating on elements of a list is usually done calling theeach andeachWithIndex methods, which execute code on eachitem of a list:

[1, 2, 3].each {    println "Item: $it" // `it` is an implicit parameter corresponding to the current element}['a', 'b', 'c'].eachWithIndex { it, i -> // `it` is the current element, while `i` is the index    println "$i: $it"}

In addition to iterating, it is often useful to create a new list by transforming each of its elements intosomething else. This operation, often called mapping, is done in Groovy thanks to thecollect method:

assert [1, 2, 3].collect { it * 2 } == [2, 4, 6]// shortcut syntax instead of collectassert [1, 2, 3]*.multiply(2) == [1, 2, 3].collect { it.multiply(2) }def list = [0]// it is possible to give `collect` the list which collects the elementsassert [1, 2, 3].collect(list) { it * 2 } == [0, 2, 4, 6]assert list == [0, 2, 4, 6]
Manipulating lists
Filtering and searching

TheGroovy development kit contains a lot of methods on collections that enhancethe standard collections with pragmatic methods, some of which are illustrated here:

assert [1, 2, 3].find { it > 1 } == 2           // find 1st element matching criteriaassert [1, 2, 3].findAll { it > 1 } == [2, 3]   // find all elements matching critieriaassert ['a', 'b', 'c', 'd', 'e'].findIndexOf {      // find index of 1st element matching criteria    it in ['c', 'e', 'g']} == 2assert ['a', 'b', 'c', 'd', 'c'].indexOf('c') == 2  // index returnedassert ['a', 'b', 'c', 'd', 'c'].indexOf('z') == -1 // index -1 means value not in listassert ['a', 'b', 'c', 'd', 'c'].lastIndexOf('c') == 4assert [1, 2, 3].every { it < 5 }               // returns true if all elements match the predicateassert ![1, 2, 3].every { it < 3 }assert [1, 2, 3].any { it > 2 }                 // returns true if any element matches the predicateassert ![1, 2, 3].any { it > 3 }assert [1, 2, 3, 4, 5, 6].sum() == 21                // sum anything with a plus() methodassert ['a', 'b', 'c', 'd', 'e'].sum {    it == 'a' ? 1 : it == 'b' ? 2 : it == 'c' ? 3 : it == 'd' ? 4 : it == 'e' ? 5 : 0    // custom value to use in sum} == 15assert ['a', 'b', 'c', 'd', 'e'].sum { ((char) it) - ((char) 'a') } == 10assert ['a', 'b', 'c', 'd', 'e'].sum() == 'abcde'assert [['a', 'b'], ['c', 'd']].sum() == ['a', 'b', 'c', 'd']// an initial value can be providedassert [].sum(1000) == 1000assert [1, 2, 3].sum(1000) == 1006assert [1, 2, 3].join('-') == '1-2-3'           // String joiningassert [1, 2, 3].inject('counting: ') {    str, item -> str + item                     // reduce operation} == 'counting: 123'assert [1, 2, 3].inject(0) { count, item ->    count + item} == 6

And here is idiomatic Groovy code for finding the maximum and minimum in a collection:

def list = [9, 4, 2, 10, 5]assert list.max() == 10assert list.min() == 2// we can also compare single characters, as anything comparableassert ['x', 'y', 'a', 'z'].min() == 'a'// we can use a closure to specify the sorting behaviourdef list2 = ['abc', 'z', 'xyzuvw', 'Hello', '321']assert list2.max { it.size() } == 'xyzuvw'assert list2.min { it.size() } == 'z'

In addition to closures, you can use aComparator to define the comparison criteria:

Comparator mc = { a, b -> a == b ? 0 : (a < b ? -1 : 1) }def list = [7, 4, 9, -6, -1, 11, 2, 3, -9, 5, -13]assert list.max(mc) == 11assert list.min(mc) == -13Comparator mc2 = { a, b -> a == b ? 0 : (Math.abs(a) < Math.abs(b)) ? -1 : 1 }assert list.max(mc2) == -13assert list.min(mc2) == -1assert list.max { a, b -> a.equals(b) ? 0 : Math.abs(a) < Math.abs(b) ? -1 : 1 } == -13assert list.min { a, b -> a.equals(b) ? 0 : Math.abs(a) < Math.abs(b) ? -1 : 1 } == -1
Adding or removing elements

We can use[] to assign a new empty list and<< to append items to it:

def list = []assert list.emptylist << 5assert list.size() == 1list << 7 << 'i' << 11assert list == [5, 7, 'i', 11]list << ['m', 'o']assert list == [5, 7, 'i', 11, ['m', 'o']]//first item in chain of << is target listassert ([1, 2] << 3 << [4, 5] << 6) == [1, 2, 3, [4, 5], 6]//using leftShift is equivalent to using <<assert ([1, 2, 3] << 4) == ([1, 2, 3].leftShift(4))

We can add to a list in many ways:

assert [1, 2] + 3 + [4, 5] + 6 == [1, 2, 3, 4, 5, 6]// equivalent to calling the `plus` methodassert [1, 2].plus(3).plus([4, 5]).plus(6) == [1, 2, 3, 4, 5, 6]def a = [1, 2, 3]a += 4      // creates a new list and assigns it to `a`a += [5, 6]assert a == [1, 2, 3, 4, 5, 6]assert [1, *[222, 333], 456] == [1, 222, 333, 456]assert [*[1, 2, 3]] == [1, 2, 3]assert [1, [2, 3, [4, 5], 6], 7, [8, 9]].flatten() == [1, 2, 3, 4, 5, 6, 7, 8, 9]def list = [1, 2]list.add(3)list.addAll([5, 4])assert list == [1, 2, 3, 5, 4]list = [1, 2]list.add(1, 3) // add 3 just before index 1assert list == [1, 3, 2]list.addAll(2, [5, 4]) //add [5,4] just before index 2assert list == [1, 3, 5, 4, 2]list = ['a', 'b', 'z', 'e', 'u', 'v', 'g']list[8] = 'x' // the [] operator is growing the list as needed// nulls inserted if requiredassert list == ['a', 'b', 'z', 'e', 'u', 'v', 'g', null, 'x']

It is however important that the+ operator on a list isnot mutating. Compared to<<, it will create a newlist, which is often not what you want and can lead to performance issues.

TheGroovy development kit also contains methods allowing you to easily remove elements from a list by value:

assert ['a','b','c','b','b'] - 'c' == ['a','b','b','b']assert ['a','b','c','b','b'] - 'b' == ['a','c']assert ['a','b','c','b','b'] - ['b','c'] == ['a']def list = [1,2,3,4,3,2,1]list -= 3           // creates a new list by removing `3` from the original oneassert list == [1,2,4,2,1]assert ( list -= [2,4] ) == [1,1]

It is also possible to remove an element by passing its index to theremove method, in which case the list is mutated:

def list = ['a','b','c','d','e','f','b','b','a']assert list.remove(2) == 'c'        // remove the third element, and return itassert list == ['a','b','d','e','f','b','b','a']

In case you only want to remove the first element having the same value in a list, instead of removing allelements, you can call theremove method passing the value:

def list= ['a','b','c','b','b']assert list.remove('c')             // remove 'c', and return true because element removedassert list.remove('b')             // remove first 'b', and return true because element removedassert ! list.remove('z')           // return false because no elements removedassert list == ['a','b','b']

As you can see, there are tworemove methods available. One that takes an integer and removes an elementby its index, and another that will remove the first element that matches the passed value. So what should wedo when we have a list of integers? In this case, you may wish to useremoveAt to remove an element by itsindex, andremoveElement to remove the first element that matches a value.

def list = [1,2,3,4,5,6,2,2,1]assert list.remove(2) == 3          // this removes the element at index 2, and returns itassert list == [1,2,4,5,6,2,2,1]assert list.removeElement(2)        // remove first 2 and return trueassert list == [1,4,5,6,2,2,1]assert ! list.removeElement(8)      // return false because 8 is not in the listassert list == [1,4,5,6,2,2,1]assert list.removeAt(1) == 4        // remove element at index 1, and return itassert list == [1,5,6,2,2,1]

Of course,removeAt andremoveElement will work with lists of any type.

Additionally, removing all the elements in a list can be done by calling theclear method:

def list= ['a',2,'c',4]list.clear()assert list == []
Set operations

TheGroovy development kit also includes methods making it easy to reason on sets:

assert 'a' in ['a','b','c']             // returns true if an element belongs to the listassert ['a','b','c'].contains('a')      // equivalent to the `contains` method in Javaassert [1,3,4].containsAll([1,4])       // `containsAll` will check that all elements are foundassert [1,2,3,3,3,3,4,5].count(3) == 4  // count the number of elements which have some valueassert [1,2,3,3,3,3,4,5].count {    it%2==0                             // count the number of elements which match the predicate} == 2assert [1,2,4,6,8,10,12].intersect([1,3,6,9,12]) == [1,6,12]assert [1,2,3].disjoint( [4,6,9] )assert ![1,2,3].disjoint( [2,4,6] )
Sorting

Working with collections often implies sorting. Groovy offers a variety of options to sort lists,from using closures to comparators, as in the following examples:

assert [6, 3, 9, 2, 7, 1, 5].sort() == [1, 2, 3, 5, 6, 7, 9]def list = ['abc', 'z', 'xyzuvw', 'Hello', '321']assert list.sort {    it.size()} == ['z', 'abc', '321', 'Hello', 'xyzuvw']def list2 = [7, 4, -6, -1, 11, 2, 3, -9, 5, -13]assert list2.sort { a, b -> a == b ? 0 : Math.abs(a) < Math.abs(b) ? -1 : 1 } ==        [-1, 2, 3, 4, 5, -6, 7, -9, 11, -13]Comparator mc = { a, b -> a == b ? 0 : Math.abs(a) < Math.abs(b) ? -1 : 1 }// JDK 8+ only// list2.sort(mc)// assert list2 == [-1, 2, 3, 4, 5, -6, 7, -9, 11, -13]def list3 = [6, -3, 9, 2, -7, 1, 5]Collections.sort(list3)assert list3 == [-7, -3, 1, 2, 5, 6, 9]Collections.sort(list3, mc)assert list3 == [1, 2, -3, 5, 6, -7, 9]
Duplicating elements

TheGroovy development kit also takes advantage of operator overloading to provide methods allowing duplication of elementsof a list:

assert [1, 2, 3] * 3 == [1, 2, 3, 1, 2, 3, 1, 2, 3]assert [1, 2, 3].multiply(2) == [1, 2, 3, 1, 2, 3]assert Collections.nCopies(3, 'b') == ['b', 'b', 'b']// nCopies from the JDK has different semantics than multiply for listsassert Collections.nCopies(2, [1, 2]) == [[1, 2], [1, 2]] //not [1,2,1,2]
Maps
Map literals

In Groovy, maps (also known as associative arrays) can be created using the map literal syntax:[:]:

def map = [name: 'Gromit', likes: 'cheese', id: 1234]assert map.get('name') == 'Gromit'assert map.get('id') == 1234assert map['name'] == 'Gromit'assert map['id'] == 1234assert map instanceof java.util.Mapdef emptyMap = [:]assert emptyMap.size() == 0emptyMap.put("foo", 5)assert emptyMap.size() == 1assert emptyMap.get("foo") == 5

Map keys are strings by default:[a:1] is equivalent to['a':1]. This can be confusing if you define a variablenameda and that you want thevalue ofa to be the key in your map. If this is the case, then youmust escapethe key by adding parenthesis, like in the following example:

def a = 'Bob'def ages = [a: 43]assert ages['Bob'] == null // `Bob` is not foundassert ages['a'] == 43     // because `a` is a literal!ages = [(a): 43]            // now we escape `a` by using parenthesisassert ages['Bob'] == 43   // and the value is found!

In addition to map literals, it is possible, to get a new copy of a map, to clone it:

def map = [        simple : 123,        complex: [a: 1, b: 2]]def map2 = map.clone()assert map2.get('simple') == map.get('simple')assert map2.get('complex') == map.get('complex')map2.get('complex').put('c', 3)assert map.get('complex').get('c') == 3

The resulting map is ashallow copy of the original one, as illustrated in the previous example.

Map property notation

Maps also act like beans so you can use the property notation to get/setitems inside theMap as long as the keys are strings which are validGroovy identifiers:

def map = [name: 'Gromit', likes: 'cheese', id: 1234]assert map.name == 'Gromit'     // can be used instead of map.get('name')assert map.id == 1234def emptyMap = [:]assert emptyMap.size() == 0emptyMap.foo = 5assert emptyMap.size() == 1assert emptyMap.foo == 5

Note: by designmap.foo will always look for the keyfoo in the map. Thismeansfoo.class will returnnull on a map that doesn’t contain theclass key. Should you really want to knowthe class, then you must usegetClass():

def map = [name: 'Gromit', likes: 'cheese', id: 1234]assert map.class == nullassert map.get('class') == nullassert map.getClass() == LinkedHashMap // this is probably what you wantmap = [1      : 'a',       (true) : 'p',       (false): 'q',       (null) : 'x',       'null' : 'z']assert map.containsKey(1) // 1 is not an identifier so used as isassert map.true == nullassert map.false == nullassert map.get(true) == 'p'assert map.get(false) == 'q'assert map.null == 'z'assert map.get(null) == 'x'
Iterating on maps

As usual in theGroovy development kit, idiomatic iteration on maps makes use of theeach andeachWithIndex methods.It’s worth noting that maps created using the map literal notation areordered, that is to say that if you iterateon map entries, it is guaranteed that the entries will be returned in the same order they were added in the map.

def map = [        Bob  : 42,        Alice: 54,        Max  : 33]// `entry` is a map entrymap.each { entry ->    println "Name: $entry.key Age: $entry.value"}// `entry` is a map entry, `i` the index in the mapmap.eachWithIndex { entry, i ->    println "$i - Name: $entry.key Age: $entry.value"}// Alternatively you can use key and value directlymap.each { key, value ->    println "Name: $key Age: $value"}// Key, value and i as the index in the mapmap.eachWithIndex { key, value, i ->    println "$i - Name: $key Age: $value"}
Manipulating maps
Adding or removing elements

Adding an element to a map can be done either using theput method, the subscript operator or usingputAll:

def defaults = [1: 'a', 2: 'b', 3: 'c', 4: 'd']def overrides = [2: 'z', 5: 'x', 13: 'x']def result = new LinkedHashMap(defaults)result.put(15, 't')result[17] = 'u'result.putAll(overrides)assert result == [1: 'a', 2: 'z', 3: 'c', 4: 'd', 5: 'x', 13: 'x', 15: 't', 17: 'u']

Removing all the elements of a map can be done by calling theclear method:

def m = [1:'a', 2:'b']assert m.get(1) == 'a'm.clear()assert m == [:]

Maps generated using the map literal syntax are using the objectequals andhashcode methods. This means thatyou shouldnever use an object which hash code is subject to change over time, or you wouldn’t be able to getthe associated value back.

It is also worth noting that you shouldnever use aGString as the key of a map, because the hash code of aGStringis not the same as the hash code of an equivalentString:

def key = 'some key'def map = [:]def gstringKey = "${key.toUpperCase()}"map.put(gstringKey,'value')assert map.get('SOME KEY') == null
Keys, values and entries

We can inspect the keys, values, and entries in a view:

def map = [1:'a', 2:'b', 3:'c']def entries = map.entrySet()entries.each { entry ->  assert entry.key in [1,2,3]  assert entry.value in ['a','b','c']}def keys = map.keySet()assert keys == [1,2,3] as Set

Mutating values returned by the view (be it a map entry, a key or a value) is highly discouraged because successof the operation directly depends on the type of the map being manipulated. In particular, Groovy relies on collectionsfrom the JDK that in general make no guarantee that a collection can safely be manipulated throughkeySet,entrySet, orvalues.

Filtering and searching

TheGroovy development kit contains filtering, searching and collecting methods similar to those foundforlists:

def people = [    1: [name:'Bob', age: 32, gender: 'M'],    2: [name:'Johnny', age: 36, gender: 'M'],    3: [name:'Claire', age: 21, gender: 'F'],    4: [name:'Amy', age: 54, gender:'F']]def bob = people.find { it.value.name == 'Bob' } // find a single entrydef females = people.findAll { it.value.gender == 'F' }// both return entries, but you can use collect to retrieve the ages for exampledef ageOfBob = bob.value.agedef agesOfFemales = females.collect {    it.value.age}assert ageOfBob == 32assert agesOfFemales == [21,54]// but you could also use a key/pair value as the parameters of the closuresdef agesOfMales = people.findAll { id, person ->    person.gender == 'M'}.collect { id, person ->    person.age}assert agesOfMales == [32, 36]// `every` returns true if all entries match the predicateassert people.every { id, person ->    person.age > 18}// `any` returns true if any entry matches the predicateassert people.any { id, person ->    person.age == 54}
Grouping

We can group a list into a map using some criteria:

assert ['a', 7, 'b', [2, 3]].groupBy {    it.class} == [(String)   : ['a', 'b'],      (Integer)  : [7],      (ArrayList): [[2, 3]]]assert [        [name: 'Clark', city: 'London'], [name: 'Sharma', city: 'London'],        [name: 'Maradona', city: 'LA'], [name: 'Zhang', city: 'HK'],        [name: 'Ali', city: 'HK'], [name: 'Liu', city: 'HK'],].groupBy { it.city } == [        London: [[name: 'Clark', city: 'London'],                 [name: 'Sharma', city: 'London']],        LA    : [[name: 'Maradona', city: 'LA']],        HK    : [[name: 'Zhang', city: 'HK'],                 [name: 'Ali', city: 'HK'],                 [name: 'Liu', city: 'HK']],]
Ranges

Ranges allow you to create a list of sequential values. These can beused asList sinceRange extendsjava.util.List.

Ranges defined with the.. notation are inclusive (that is the listcontains the from and to value).

Ranges defined with the..< notation are half-open, they include thefirst value but not the last value.

Ranges defined with the<.. notation are also half-open, they include thelast value but not the first value.

Ranges defined with the<..< notation are full-open, they do not include thefirst value nor the last value.

// an inclusive rangedef range = 5..8assert range.size() == 4assert range.get(2) == 7assert range[2] == 7assert range instanceof java.util.Listassert range.contains(5)assert range.contains(8)// lets use a half-open rangerange = 5..<8assert range.size() == 3assert range.get(2) == 7assert range[2] == 7assert range instanceof java.util.Listassert range.contains(5)assert !range.contains(8)//get the end points of the range without using indexesrange = 1..10assert range.from == 1assert range.to == 10

Note that int ranges are implemented efficiently, creating a lightweightJava object containing a from and to value.

Ranges can be used for any Java object which implements java.lang.Comparablefor comparison and also have methodsnext() andprevious() to return thenext / previous item in the range. For example, you can create a range ofString elements:

// an inclusive rangedef range = 'a'..'d'assert range.size() == 4assert range.get(2) == 'c'assert range[2] == 'c'assert range instanceof java.util.Listassert range.contains('a')assert range.contains('d')assert !range.contains('e')

You can iterate on a range using a classicfor loop:

for (i in 1..10) {    println "Hello ${i}"}

but alternatively you can achieve the same effect in a more Groovy idiomatic style, by iterating a rangewitheach method:

(1..10).each { i ->    println "Hello ${i}"}

Ranges can be also used in theswitch statement:

switch (years) {    case 1..10: interestRate = 0.076; break;    case 11..25: interestRate = 0.052; break;    default: interestRate = 0.037;}
Syntax enhancements for collections
GPath support

Thanks to the support of property notation for both lists and maps, Groovy provides syntactic sugar making itreally easy to deal with nested collections, as illustrated in the following examples:

def listOfMaps = [['a': 11, 'b': 12], ['a': 21, 'b': 22]]assert listOfMaps.a == [11, 21] //GPath notationassert listOfMaps*.a == [11, 21] //spread dot notationlistOfMaps = [['a': 11, 'b': 12], ['a': 21, 'b': 22], null]assert listOfMaps*.a == [11, 21, null] // caters for null valuesassert listOfMaps*.a == listOfMaps.collect { it?.a } //equivalent notation// But this will only collect non-null valuesassert listOfMaps.a == [11,21]
Spread operator

The spread operator can be used to "inline" a collection into another. It is syntactic sugar which often avoids callstoputAll and facilitates the realization of one-liners:

assert [ 'z': 900,         *: ['a': 100, 'b': 200], 'a': 300] == ['a': 300, 'b': 200, 'z': 900]//spread map notation in map definitionassert [*: [3: 3, *: [5: 5]], 7: 7] == [3: 3, 5: 5, 7: 7]def f = { [1: 'u', 2: 'v', 3: 'w'] }assert [*: f(), 10: 'zz'] == [1: 'u', 10: 'zz', 2: 'v', 3: 'w']//spread map notation in function argumentsf = { map -> map.c }assert f(*: ['a': 10, 'b': 20, 'c': 30], 'e': 50) == 30f = { m, i, j, k -> [m, i, j, k] }//using spread map notation with mixed unnamed and named argumentsassert f('e': 100, *[4, 5], *: ['a': 10, 'b': 20, 'c': 30], 6) ==        [["e": 100, "b": 20, "c": 30, "a": 10], 4, 5, 6]
The star-dot `*.' operator

The "star-dot" operator is a shortcut operator allowing you to call a method or a property on all elements of acollection:

assert [1, 3, 5] == ['a', 'few', 'words']*.size()class Person {    String name    int age}def persons = [new Person(name:'Hugo', age:17), new Person(name:'Sandra',age:19)]assert [17, 19] == persons*.age
Slicing with the subscript operator

You can index into lists, arrays, maps using the subscript expression. It is interesting that stringsare considered as special kinds of collections in that context:

def text = 'nice cheese gromit!'def x = text[2]assert x == 'c'assert x.class == Stringdef sub = text[5..10]assert sub == 'cheese'def list = [10, 11, 12, 13]def answer = list[2,3]assert answer == [12,13]

Notice that you can use ranges to extract part of a collection:

list = 100..200sub = list[1, 3, 20..25, 33]assert sub == [101, 103, 120, 121, 122, 123, 124, 125, 133]

The subscript operator can be used to update an existing collection (for collection type which are not immutable):

list = ['a','x','x','d']list[1..2] = ['b','c']assert list == ['a','b','c','d']

It is worth noting that negative indices are allowed, to extract more easily from the end of a collection:

text = "nice cheese gromit!"x = text[-1]assert x == "!"

You can use negative indices to count from the end of the List, array,String etc.

def name = text[-7..-2]assert name == "gromit"

Eventually, if you use a backwards range (the starting index is greater thanthe end index), then the answer is reversed.

text = "nice cheese gromit!"name = text[3..1]assert name == "eci"
Enhanced Collection Methods

In addition tolists,maps orranges, Groovy offersa lot of additional methods for filtering, collecting, grouping, counting, …​ which are directly available on eithercollections or more easily iterables.

In particular, we invite you to read theGroovy development kit API docs and specifically:

  • methods added toIterable can be foundhere

  • methods added toIterator can be foundhere

  • methods added toCollection can be foundhere

  • methods added toList can be foundhere

  • methods added toMap can be foundhere

3.3.3. Working with arrays

Groovy provides array support based on Java arrays with several extensions found in theGroovy development kit. The overallintention is that whether you are using an array or a collection, the code for working with the aggregate remains the same.

Arrays
Array literals

You can create arrays as follows. Notice that[] is also used as the empty arrayexpression when given an explicit array type.

Integer[] nums = [5, 6, 7, 8]assert nums[1] == 6assert nums.getAt(2) == 7                // alternative syntaxassert nums[-1] == 8                     // negative indicesassert nums instanceof Integer[]int[] primes = [2, 3, 5, 7]              // primitivesassert primes instanceof int[]def evens = new int[]{2, 4, 6}           // alt syntax 1assert evens instanceof int[]def odds = [1, 3, 5] as int[]            // alt syntax 2assert odds instanceof int[]// empty array examplesInteger[] emptyNums = []assert emptyNums instanceof Integer[] && emptyNums.size() == 0def emptyStrings = new String[]{}        // alternative syntax 1assert emptyStrings instanceof String[] && emptyStrings.size() == 0var emptyObjects = new Object[0]         // alternative syntax 2assert emptyObjects instanceof Object[] && emptyObjects.size() == 0
Iterating on a list

Iterating on elements of a list is usually done calling theeach andeachWithIndex methods, which execute code on eachitem of a list:

String[] vowels = ['a', 'e', 'i', 'o', 'u']var result = ''vowels.each {    result += it}assert result == 'aeiou'result = ''vowels.eachWithIndex { v, i ->    result += v * i         // index starts from 0}assert result == 'eiiooouuuu'
Other useful methods

There are numerous other GDK methods for working with arrays.Just be a little careful to read the documentation.For collections, there are some mutating methods which alterthe original collection and others which produce new collections,leaving the original untouched.Since arrays are of a fixed size, we wouldn’t expect mutatingmethods which altered an array’s size. Often instead, such methods returncollections. Here are some interesting array GDK methods:

int[] nums = [1, 2, 3]def doubled = nums.collect { it * 2 }assert doubled == [2, 4, 6] && doubled instanceof Listdef tripled = nums*.multiply(3)assert tripled == [3, 6, 9] && doubled instanceof Listassert nums.any{ it > 2 }assert nums.every{ it < 4 }assert nums.average() == 2assert nums.min() == 1assert nums.max() == 3assert nums.sum() == 6assert nums.indices == [0, 1, 2]assert nums.swap(0, 2) == [3, 2, 1] as int[]

3.3.4. Working with legacy Date/Calendar types

Thegroovy-dateutil module supports numerous extensions for working withJava’s classicDate andCalendar classes.

You can access the properties of aDate orCalendar using the normal array index notationwith the constant field numbers from theCalendar class as shown in the following example:

import static java.util.Calendar.*(1)def cal = Calendar.instancecal[YEAR] = 2000(2)cal[MONTH] = JANUARY(2)cal[DAY_OF_MONTH] = 1(2)assert cal[DAY_OF_WEEK] == SATURDAY(3)
1Import the constants
2Setting the calendar’s year, month and day of month
3Accessing the calendar’s day of week

Groovy supports arithmetic on and iteration betweenDate andCalendar instances as shown in the following example:

def utc = TimeZone.getTimeZone('UTC')Date date = Date.parse("yyyy-MM-dd HH:mm", "2010-05-23 09:01", utc)def prev = date - 1def next = date + 1def diffInDays = next - prevassert diffInDays == 2int count = 0prev.upto(next) { count++ }assert count == 3

You can parse strings into dates and output dates into formatted strings:

def orig = '2000-01-01'def newYear = Date.parse('yyyy-MM-dd', orig)assert newYear[DAY_OF_WEEK] == SATURDAYassert newYear.format('yyyy-MM-dd') == origassert newYear.format('dd/MM/yyyy') == '01/01/2000'

You can also create a new Date or Calendar based on an existing one:

def newYear = Date.parse('yyyy-MM-dd', '2000-01-01')def newYearsEve = newYear.copyWith(    year: 1999,    month: DECEMBER,    dayOfMonth: 31)assert newYearsEve[DAY_OF_WEEK] == FRIDAY

3.3.5. Working with Date/Time types

Thegroovy-datetime module supports numerous extensions for working withtheDate/Time APIintroduced in Java 8. This documentation refers to the data types defined by this API as"JSR 310 types."

Formatting and parsing

A common use case when working with date/time types is to convert them to Strings (formatting)and from Strings (parsing). Groovy provides these additional formatting methods:

MethodDescriptionExample

getDateString()

ForLocalDate andLocalDateTime, formats withDateTimeFormatter.ISO_LOCAL_DATE

2018-03-10

ForOffsetDateTime, formats withDateTimeFormatter.ISO_OFFSET_DATE

2018-03-10+04:00

ForZonedDateTime, formats withDateTimeFormatter.ISO_LOCAL_DATEand appends theZoneId short name

2018-03-10EST

getDateTimeString()

ForLocalDateTime, formats withDateTimeFormatter.ISO_LOCAL_DATE_TIME

2018-03-10T20:30:45

ForOffsetDateTime, formats withDateTimeFormatter.ISO_OFFSET_DATE_TIME

2018-03-10T20:30:45+04:00

ForZonedDateTime, formats withDateTimeFormatter.ISO_LOCAL_DATE_TIMEand appends theZoneId short name

2018-03-10T20:30:45EST

getTimeString()

ForLocalTime andLocalDateTime, formats withDateTimeFormatter.ISO_LOCAL_TIME

20:30:45

ForOffsetTime andOffsetDateTime, formats withDateTimeFormatter.ISO_OFFSET_TIMEformatter

20:30:45+04:00

ForZonedDateTime, formats withDateTimeFormatter.ISO_LOCAL_TIMEand appends theZoneId short name

20:30:45EST

format(FormatStyle style)

ForLocalTime andOffsetTime, formats withDateTimeFormatter.ofLocalizedTime(style)

4:30 AM (with styleFormatStyle.SHORT, e.g.)

ForLocalDate, formats withDateTimeFormatter.ofLocalizedDate(style)

Saturday, March 10, 2018 (with styleFormatStyle.FULL, e.g.)

ForLocalDateTime,OffsetDateTime, andZonedDateTime formats withDateTimeFormatter.ofLocalizedDateTime(style)

Mar 10, 2019 4:30:45 AM (with styleFormatStyle.MEDIUM, e.g.)

format(String pattern)

Formats withDateTimeFormatter.ofPattern(pattern)

03/10/2018 (with pattern ’MM/dd/yyyy', e.g.)

For parsing, Groovy adds a staticparse method to many of the JSR 310 types. The methodtakes two arguments: the value to be formatted and the pattern to use. The pattern isdefined by thejava.time.format.DateTimeFormatter API.As an example:

def date = LocalDate.parse('Jun 3, 04', 'MMM d, yy')assert date == LocalDate.of(2004, Month.JUNE, 3)def time = LocalTime.parse('4:45', 'H:mm')assert time == LocalTime.of(4, 45, 0)def offsetTime = OffsetTime.parse('09:47:51-1234', 'HH:mm:ssZ')assert offsetTime == OffsetTime.of(9, 47, 51, 0, ZoneOffset.ofHoursMinutes(-12, -34))def dateTime = ZonedDateTime.parse('2017/07/11 9:47PM Pacific Standard Time', 'yyyy/MM/dd h:mma zzzz')assert dateTime == ZonedDateTime.of(        LocalDate.of(2017, 7, 11),        LocalTime.of(21, 47, 0),        ZoneId.of('America/Los_Angeles'))

Note that theseparse methods have a different argument ordering than the staticparse method Groovy added tojava.util.Date.This was done to be consistent with the existingparse methods of the Date/Time API.

Manipulating date/time
Addition and subtraction

Temporal types haveplus andminus methods for adding or subtracting a providedjava.time.temporal.TemporalAmount argument. Because Groovy maps the+ and- operatorsto single-argument methods of these names, a more natural expression syntax can be used to add and subtract.

def aprilFools = LocalDate.of(2018, Month.APRIL, 1)def nextAprilFools = aprilFools + Period.ofDays(365) // add 365 daysassert nextAprilFools.year == 2019def idesOfMarch = aprilFools - Period.ofDays(17) // subtract 17 daysassert idesOfMarch.dayOfMonth == 15assert idesOfMarch.month == Month.MARCH

Groovy provides additionalplus andminus methods that accept an integer argument,enabling the above to be rewritten more succinctly:

def nextAprilFools = aprilFools + 365 // add 365 daysdef idesOfMarch = aprilFools - 17 // subtract 17 days

The unit of these integers depends on the JSR 310 type operand. As evident above,integers used withChronoLocalDate types likeLocalDate have a unit ofdays.Integers used withYear andYearMonth have a unit ofyears andmonths, respectively.All other types have a unit ofseconds,such asLocalTime, for instance:

def mars = LocalTime.of(12, 34, 56) // 12:34:56 pmdef thirtySecondsToMars = mars - 30 // go back 30 secondsassert thirtySecondsToMars.second == 26
Multiplication and division

The* operator can be used to multiplyPeriod andDuration instances by aninteger value; the/ operator can be used to divideDuration instances by an integer value.

def period = Period.ofMonths(1) * 2 // a 1-month period times 2assert period.months == 2def duration = Duration.ofSeconds(10) / 5// a 10-second duration divided by 5assert duration.seconds == 2
Incrementing and decrementing

The++ and-- operators can be used increment and decrement date/time values by one unit. Since the JSR 310 typesare immutable, the operation will create a new instance with the incremented/decremented value and reassign it to thereference.

def year = Year.of(2000)--year // decrement by one yearassert year.value == 1999def offsetTime = OffsetTime.of(0, 0, 0, 0, ZoneOffset.UTC) // 00:00:00.000 UTCoffsetTime++ // increment by one secondassert offsetTime.second == 1
Negation

TheDuration andPeriod types represent a negative or positive length of time.These can be negated with the unary- operator.

def duration = Duration.ofSeconds(-15)def negated = -durationassert negated.seconds == 15
Interacting with date/time values
Property notation

ThegetLong(TemporalField)method ofTemporalAccessor types (e.g.LocalDate,LocalTime,ZonedDateTime, etc.) and theget(TemporalUnit)method ofTemporalAmount types (namelyPeriod andDuration), can be invoked withGroovy’s property notation. For example:

def date = LocalDate.of(2018, Month.MARCH, 12)assert date[ChronoField.YEAR] == 2018assert date[ChronoField.MONTH_OF_YEAR] == Month.MARCH.valueassert date[ChronoField.DAY_OF_MONTH] == 12assert date[ChronoField.DAY_OF_WEEK] == DayOfWeek.MONDAY.valuedef period = Period.ofYears(2).withMonths(4).withDays(6)assert period[ChronoUnit.YEARS] == 2assert period[ChronoUnit.MONTHS] == 4assert period[ChronoUnit.DAYS] == 6
Ranges,upto, anddownto

The JSR 310 types can be used with therange operator.The following example iterates between today and theLocalDate six days from now,printing out the day of the week for each iteration. As both range bounds are inclusive,this prints all seven days of the week.

def start = LocalDate.now()def end = start + 6 // 6 days later(start..end).each { date ->    println date.dayOfWeek}

Theupto method will accomplish the same as the range in the above example.Theupto method iterates from the earlierstart value (inclusive) to the laterend value(also inclusive), calling the closure with the incrementednext value once per iteration.

def start = LocalDate.now()def end = start + 6 // 6 days laterstart.upto(end) { next ->    println next.dayOfWeek}

Thedownto method iterates in the opposite direction, from a laterstart valueto an earlierend value.

The unit of iteration forupto,downto, and ranges is the same as the unit for additionand subtraction:LocalDate iterates by one day at a time,YearMonth iterates by one month,Year by one year, and everything else by one second.Both methods also support an optional aTemporalUnit argument to change the unit ofiteration.

Consider the following example, where March 1st, 2018 is iterated up to March 2nd, 2018using an iteration unit ofmonths.

def start = LocalDate.of(2018, Month.MARCH, 1)def end = start + 1 // 1 day laterint iterationCount = 0start.upto(end, ChronoUnit.MONTHS) { next ->    println next    ++iterationCount}assert iterationCount == 1

Since thestart date is inclusive, the closure is called with anext date value of March 1st.Theupto method then increments the date by one month, yielding the date, April 1st.Because this date isafter the specifiedend date of March 2nd, the iteration stops immediately,having only called the closure once. This behavior is the same for thedownto method except thatthe iteration will stop as soon as the value ofnext becomes earlier than the targetedend date.

In short, when iterating with theupto ordownto methods with a custom unit of iteration,the current value of iteration will never exceed the end value.

Combining date/time values

The left-shift operator (<<) can be used to combine two JSR 310 types into an aggregate type.For example, aLocalDate can be left-shifted into aLocalTime to produce a compositeLocalDateTime instance.

MonthDay monthDay = Month.JUNE << 3 // June 3rdLocalDate date = monthDay << Year.of(2015) // 3-Jun-2015LocalDateTime dateTime = date << LocalTime.NOON // 3-Jun-2015 @ 12pmOffsetDateTime offsetDateTime = dateTime << ZoneOffset.ofHours(-5) // 3-Jun-2015 @ 12pm UTC-5

The left-shift operator is reflexive; the order of the operands does not matter.

def year = Year.of(2000)def month = Month.DECEMBERYearMonth a = year << monthYearMonth b = month << yearassert a == b
Creating periods and durations

The right-shift operator (>>) produces a value representing the period or duration between theoperands. ForChronoLocalDate,YearMonth, andYear, the operator yieldsaPeriod instance:

def newYears = LocalDate.of(2018, Month.JANUARY, 1)def aprilFools = LocalDate.of(2018, Month.APRIL, 1)def period = newYears >> aprilFoolsassert period instanceof Periodassert period.months == 3

The operator produces aDuration for the time-aware JSR types:

def duration = LocalTime.NOON >> (LocalTime.NOON + 30)assert duration instanceof Durationassert duration.seconds == 30

If the value on the left-hand side of the operator is earlier than the value on the right-handside, the result is positive. If the left-hand side is later than the right-hand side, theresult is negative:

def decade = Year.of(2010) >> Year.of(2000)assert decade.years == -10
Converting between legacy and JSR 310 types

Despite the shortcomings ofDate,Calendar, andTimeZone types in thejava.util packagethey are fairly common in Java APIs (at least in those prior to Java 8).To accommodate use of such APIs, Groovy provides methods for converting between theJSR 310 types and legacy types.

Most JSR types have been fitted withtoDate() andtoCalendar() methods forconverting to relatively equivalentjava.util.Date andjava.util.Calendar values.BothZoneId andZoneOffset have been given atoTimeZone() method for converting tojava.util.TimeZone.

// LocalDate to java.util.Datedef valentines = LocalDate.of(2018, Month.FEBRUARY, 14)assert valentines.toDate().format('MMMM dd, yyyy') == 'February 14, 2018'// LocalTime to java.util.Datedef noon = LocalTime.of(12, 0, 0)assert noon.toDate().format('HH:mm:ss') == '12:00:00'// ZoneId to java.util.TimeZonedef newYork = ZoneId.of('America/New_York')assert newYork.toTimeZone() == TimeZone.getTimeZone('America/New_York')// ZonedDateTime to java.util.Calendardef valAtNoonInNY = ZonedDateTime.of(valentines, noon, newYork)assert valAtNoonInNY.toCalendar().getTimeZone().toZoneId() == newYork

Note that when converting to a legacy type:

  • Nanosecond values are truncated to milliseconds. ALocalTime, for example, with aChronoUnit.NANOS valueof 999,999,999 nanoseconds translates to 999 milliseconds.

  • When converting the "local" types (LocalDate,LocalTime, andLocalDateTime), the time zone of thereturnedDate orCalendar will be the system default.

  • When converting a time-only type (LocalTime orOffsetTime), the year/month/day of theDate orCalendar is setto the current date.

  • When converting a date-only type (LocalDate), the time value of theDate orCalendar will be cleared,i.e.00:00:00.000.

  • When converting anOffsetDateTime to aCalendar, only the hours and minutes of theZoneOffset conveyinto the correspondingTimeZone. Fortunately, Zone Offsets with non-zero seconds are rare.

Groovy has added a number of methods toDate andCalendarfor converting into the various JSR 310 types:

Date legacy = Date.parse('yyyy-MM-dd HH:mm:ss.SSS', '2010-04-03 10:30:58.999')assert legacy.toLocalDate() == LocalDate.of(2010, 4, 3)assert legacy.toLocalTime() == LocalTime.of(10, 30, 58, 999_000_000) // 999M ns = 999msassert legacy.toOffsetTime().hour == 10assert legacy.toYear() == Year.of(2010)assert legacy.toMonth() == Month.APRILassert legacy.toDayOfWeek() == DayOfWeek.SATURDAYassert legacy.toMonthDay() == MonthDay.of(Month.APRIL, 3)assert legacy.toYearMonth() == YearMonth.of(2010, Month.APRIL)assert legacy.toLocalDateTime().year == 2010assert legacy.toOffsetDateTime().dayOfMonth == 3assert legacy.toZonedDateTime().zone == ZoneId.systemDefault()

3.3.6. Handy utilities

ConfigSlurper

ConfigSlurper is a utility class for reading configuration files defined in the form of Groovy scripts. Like it isthe case with Java*.properties files,ConfigSlurper allows a dot notation. But in addition, it allows for Closure scopedconfiguration values and arbitrary object types.

def config = new ConfigSlurper().parse('''    app.date = new Date()(1)    app.age  = 42    app {(2)        name = "Test${42}"    }''')assert config.app.date instanceof Dateassert config.app.age == 42assert config.app.name == 'Test42'
1Usage of the dot notation
2Usage of Closure scopes as an alternative to the dot notation

As can be seen in the above example, theparse method can be used to retrievegroovy.util.ConfigObject instances. TheConfigObject is a specializedjava.util.Map implementation that either returns the configured value or a newConfigObjectinstance but nevernull.

def config = new ConfigSlurper().parse('''    app.date = new Date()    app.age  = 42    app.name = "Test${42}"''')assert config.test != null(1)
1config.test has not been specified yet it returns aConfigObject when being called.

In the case of a dot being part of a configuration variable name, it can be escaped by using single or double quotes.

def config = new ConfigSlurper().parse('''    app."person.age"  = 42''')assert config.app."person.age" == 42

In addition,ConfigSlurper comes with support forenvironments. Theenvironments method can be used to hand overa Closure instance that itself may consist of a several sections. Let’s say we wanted to create a particular configurationvalue for the development environment. When creating theConfigSlurper instance we can use theConfigSlurper(String)constructor to specify the target environment.

def config = new ConfigSlurper('development').parse('''  environments {       development {           app.port = 8080       }       test {           app.port = 8082       }       production {           app.port = 80       }  }''')assert config.app.port == 8080
TheConfigSlurper environments aren’t restricted to any particular environment names. It solely depends on theConfigSlurper client code what value are supported and interpreted accordingly.

Theenvironments method is built-in but theregisterConditionalBlock method can be used to register other method namesin addition to theenvironments name.

def slurper = new ConfigSlurper()slurper.registerConditionalBlock('myProject', 'developers')(1)def config = slurper.parse('''  sendMail = true  myProject {       developers {           sendMail = false       }  }''')assert !config.sendMail
1Once the new block is registeredConfigSlurper can parse it.

For Java integration purposes thetoProperties method can be used to convert theConfigObject to ajava.util.Propertiesobject that might be stored to a*.properties text file. Be aware though that the configuration values are converted toString instances during adding them to the newly createdProperties instance.

def config = new ConfigSlurper().parse('''    app.date = new Date()    app.age  = 42    app {        name = "Test${42}"    }''')def properties = config.toProperties()assert properties."app.date" instanceof Stringassert properties."app.age" == '42'assert properties."app.name" == 'Test42'
Expando

TheExpando class can be used to create a dynamically expandable object. Despite its name it does not use theExpandoMetaClass underneath. EachExpando object represents a standalone, dynamically-crafted instance that can beextended with properties (or methods) at runtime.

def expando = new Expando()expando.name = 'John'assert expando.name == 'John'

A special case occurs when a dynamic property registers aClosure code block. Once being registered it can be invokedas it would be done with a method call.

def expando = new Expando()expando.toString = { -> 'John' }expando.say = { String s -> "John says: ${s}" }assert expando as String == 'John'assert expando.say('Hi') == 'John says: Hi'
Observable list, map and set

Groovy comes with observable lists, maps and sets. Each of these collections triggerjava.beans.PropertyChangeEvent events when elementsare added, removed or changed. Note that aPropertyChangeEvent is not only signalling that a certain event has occurred, moreover, it holds information on the property name and the old/new value a certain property has been changed to.

Depending on the type of change that has happened, observable collections might fire more specializedPropertyChangeEvent types. For example, adding an element to an observable list fires anObservableList.ElementAddedEvent event.

def event(1)def listener = {    if (it instanceof ObservableList.ElementEvent)  {(2)        event = it    }} as PropertyChangeListenerdef observable = [1, 2, 3] as ObservableList(3)observable.addPropertyChangeListener(listener)(4)observable.add 42(5)assert event instanceof ObservableList.ElementAddedEventdef elementAddedEvent = event as ObservableList.ElementAddedEventassert elementAddedEvent.changeType == ObservableList.ChangeType.ADDEDassert elementAddedEvent.index == 3assert elementAddedEvent.oldValue == nullassert elementAddedEvent.newValue == 42
1Declares aPropertyChangeEventListener that is capturing the fired events
2ObservableList.ElementEvent and its descendant types are relevant for this listener
3Creates anObservableList from the given list
4Registers the listener
5Triggers anObservableList.ElementAddedEvent event
Be aware that adding an element in fact causes two events to be triggered. The first is of typeObservableList.ElementAddedEvent,the second is a plainPropertyChangeEvent that informs listeners about the change of propertysize.

TheObservableList.ElementClearedEvent event type is another interesting one. Whenever multiple elements are removed, for example when callingclear(), it holds the elements being removed from the list.

def eventdef listener = {    if (it instanceof ObservableList.ElementEvent)  {        event = it    }} as PropertyChangeListenerdef observable = [1, 2, 3] as ObservableListobservable.addPropertyChangeListener(listener)observable.clear()assert event instanceof ObservableList.ElementClearedEventdef elementClearedEvent = event as ObservableList.ElementClearedEventassert elementClearedEvent.values == [1, 2, 3]assert observable.size() == 0

To get an overview of all the supported event types the reader is encouraged to have a look at the JavaDoc documentationor the source code of the observable collection in use.

ObservableMap andObservableSet come with the same concepts as we have seen forObservableList in this section.

3.4. Metaprogramming

The Groovy language supports two flavors of metaprogramming: runtime and compile-time.The first allows altering the class model and the behavior of a program at runtime while the second only occursat compile-time. Both have pros and cons that we will detail in this section.

3.4.1. Runtime metaprogramming

With runtime metaprogramming we can postpone to runtime the decision to intercept, inject and even synthesize methods of classes and interfaces. For a deep understanding of Groovy’s metaobject protocol (MOP) we need to understand Groovy objects and Groovy’s method handling.In Groovy we work with three kinds of objects: POJO, POGO and Groovy Interceptors. Groovy allows metaprogramming for all types of objects but in a different manner.

  • POJO - A regular Java object whose class can be written in Java or any other language for the JVM.

  • POGO - A Groovy object whose class is written in Groovy. It extendsjava.lang.Object and implements thegroovy.lang.GroovyObject interface by default.

  • Groovy Interceptor - A Groovy object that implements thegroovy.lang.GroovyInterceptable interface and has method-interception capability which is discussed in theGroovyInterceptable section.

For every method call Groovy checks whether the object is a POJO or a POGO. For POJOs, Groovy fetches itsMetaClass from thegroovy.lang.MetaClassRegistry and delegates method invocation to it. For POGOs, Groovy takes more steps, as illustrated in the following figure:

GroovyInterceptions
Figure 1. Groovy interception mechanism
GroovyObject interface

groovy.lang.GroovyObject is the main interface in Groovy as theObject class is in Java.GroovyObject has a default implementation in thegroovy.lang.GroovyObjectSupport class and it is responsible to transfer invocation to thegroovy.lang.MetaClass object. TheGroovyObject source looks like this:

package groovy.lang;public interface GroovyObject {    Object invokeMethod(String name, Object args);    Object getProperty(String propertyName);    void setProperty(String propertyName, Object newValue);    MetaClass getMetaClass();    void setMetaClass(MetaClass metaClass);}
invokeMethod

This method is primarily intended to be used in conjunction with theGroovyInterceptableinterface or an object’sMetaClass where it will intercept all method calls.

It is also invoked when the method called is not present on a Groovy object. Here is a simple example using anoverriddeninvokeMethod() method:

class SomeGroovyClass {    def invokeMethod(String name, Object args) {        return "called invokeMethod $name $args"    }    def test() {        return 'method exists'    }}def someGroovyClass = new SomeGroovyClass()assert someGroovyClass.test() == 'method exists'assert someGroovyClass.someMethod() == 'called invokeMethod someMethod []'

However, the use ofinvokeMethod to intercept missing methods is discouraged. In cases where the intent is to onlyintercept method calls in the case of a failed method dispatch usemethodMissinginstead.

get/setProperty

Every read access to a property can be intercepted by overriding thegetProperty() method of the current object.Here is a simple example:

class SomeGroovyClass {    def property1 = 'ha'    def field2 = 'ho'    def field4 = 'hu'    def getField1() {        return 'getHa'    }    def getProperty(String name) {        if (name != 'field3')            return metaClass.getProperty(this, name)(1)        else            return 'field3'    }}def someGroovyClass = new SomeGroovyClass()assert someGroovyClass.field1 == 'getHa'assert someGroovyClass.field2 == 'ho'assert someGroovyClass.field3 == 'field3'assert someGroovyClass.field4 == 'hu'
1Forwards the request to the getter for all properties exceptfield3.

You can intercept write access to properties by overriding thesetProperty() method:

class POGO {    String property     void setProperty(String name, Object value) {        this.@"$name" = 'overridden'    }}def pogo = new POGO()pogo.property = 'a'assert pogo.property == 'overridden'
get/setMetaClass

You can access an object’smetaClass or set your ownMetaClass implementation for changing the default interception mechanism. For example, you can write your own implementation of theMetaClass interface and assign it to objects in order to change the interception mechanism:

// getMetaclasssomeObject.metaClass// setMetaClasssomeObject.metaClass = new OwnMetaClassImplementation()
You can find an additional example in theGroovyInterceptable topic.
get/setAttribute

This functionality is related to theMetaClass implementation. In the default implementation you can access fields without invoking their getters and setters. The examples below demonstrates this approach:

class SomeGroovyClass {    def field1 = 'ha'    def field2 = 'ho'    def getField1() {        return 'getHa'    }}def someGroovyClass = new SomeGroovyClass()assert someGroovyClass.metaClass.getAttribute(someGroovyClass, 'field1') == 'ha'assert someGroovyClass.metaClass.getAttribute(someGroovyClass, 'field2') == 'ho'
class POGO {    private String field    String property1    void setProperty1(String property1) {        this.property1 = "setProperty1"    }}def pogo = new POGO()pogo.metaClass.setAttribute(pogo, 'field', 'ha')pogo.metaClass.setAttribute(pogo, 'property1', 'ho')assert pogo.field == 'ha'assert pogo.property1 == 'ho'
methodMissing

Groovy supports the concept ofmethodMissing. This method differs frominvokeMethod in that itis only invoked in the case of a failed method dispatch when no method can be found for the given name and/or thegiven arguments:

class Foo {   def methodMissing(String name, def args) {        return "this is me"   }}assert new Foo().someUnknownMethod(42l) == 'this is me'

Typically when usingmethodMissing it is possible to cache the result for the next time the same method is called.

For example, consider dynamic finders in GORM. These are implemented in terms ofmethodMissing. The code resemblessomething like this:

class GORM {   def dynamicMethods = [...] // an array of dynamic methods that use regex   def methodMissing(String name, args) {       def method = dynamicMethods.find { it.match(name) }       if(method) {          GORM.metaClass."$name" = { Object[] varArgs ->             method.invoke(delegate, name, varArgs)          }          return method.invoke(delegate,name, args)       }       else throw new MissingMethodException(name, delegate, args)   }}

Notice how, if we find a method to invoke, we then dynamically register a new method on the fly usingExpandoMetaClass.This is so that the next time the same method is called it is more efficient. This way of usingmethodMissing does not havethe overhead ofinvokeMethodand is not expensive from the second call on.

propertyMissing

Groovy supports the concept ofpropertyMissing for intercepting otherwise failing property resolution attempts. In thecase of a getter method,propertyMissing takes a singleString argument containing the property name:

class Foo {   def propertyMissing(String name) { name }}assert new Foo().boo == 'boo'

ThepropertyMissing(String) method is only called when no getter method for the given property can be found by the Groovyruntime.

For setter methods a secondpropertyMissing definition can be added that takes an additional value argument:

class Foo {   def storage = [:]   def propertyMissing(String name, value) { storage[name] = value }   def propertyMissing(String name) { storage[name] }}def f = new Foo()f.foo = "bar"assert f.foo == "bar"

As withmethodMissing it is best practice to dynamically register new properties at runtime to improve the overall lookupperformance.

static methodMissing

Static variant ofmethodMissing method can be added via theExpandoMetaClassor can be implemented at the class level with$static_methodMissing method.

class Foo {    static def $static_methodMissing(String name, Object args) {        return "Missing static method name is $name"    }}assert Foo.bar() == 'Missing static method name is bar'
static propertyMissing

Static variant ofpropertyMissing method can be added via theExpandoMetaClassor can be implemented at the class level with$static_propertyMissing method.

class Foo {    static def $static_propertyMissing(String name) {        return "Missing static property name is $name"    }}assert Foo.foobar == 'Missing static property name is foobar'
GroovyInterceptable

Thegroovy.lang.GroovyInterceptable interface is marker interface that extendsGroovyObject and is used to notify the Groovy runtime that all methods should be intercepted through the method dispatcher mechanism of the Groovy runtime.

package groovy.lang;public interface GroovyInterceptable extends GroovyObject {}

When a Groovy object implements theGroovyInterceptable interface, itsinvokeMethod() is called for any method calls. Below you can see a simple example of an object of this type:

class Interception implements GroovyInterceptable {    def definedMethod() { }    def invokeMethod(String name, Object args) {        'invokedMethod'    }}

The next piece of code is a test which shows that both calls to existing and non-existing methods will return the same value.

class InterceptableTest extends GroovyTestCase {    void testCheckInterception() {        def interception = new Interception()        assert interception.definedMethod() == 'invokedMethod'        assert interception.someMethod() == 'invokedMethod'    }}
We cannot use default groovy methods likeprintln because these methods are injected into all Groovy objects so they will be intercepted too.

If we want to intercept all method calls but do not want to implement theGroovyInterceptable interface we can implementinvokeMethod() on an object’sMetaClass.This approach works for both POGOs and POJOs, as shown by this example:

class InterceptionThroughMetaClassTest extends GroovyTestCase {    void testPOJOMetaClassInterception() {        String invoking = 'ha'        invoking.metaClass.invokeMethod = { String name, Object args ->            'invoked'        }        assert invoking.length() == 'invoked'        assert invoking.someMethod() == 'invoked'    }    void testPOGOMetaClassInterception() {        Entity entity = new Entity('Hello')        entity.metaClass.invokeMethod = { String name, Object args ->            'invoked'        }        assert entity.build(new Object()) == 'invoked'        assert entity.someMethod() == 'invoked'    }}
Additional information aboutMetaClass can be found in theMetaClasses section.
Categories

There are situations where it is useful if a classnot under control had additional methods. In order to enable thiscapability, Groovy implements a feature borrowed from Objective-C, calledCategories.

Categories are implemented with so-calledcategory classes. A category class is special in that it needs to meet certainpre-defined rules for defining extension methods.

There are a few categories that are included in the system for adding functionality to classes that make them moreusable within the Groovy environment:

Category classes aren’t enabled by default. To use the methods defined in a category class it is necessary to applythe scopeduse method that is provided by the GDK and available from inside every Groovy object instance:

use(TimeCategory)  {    println 1.minute.from.now(1)    println 10.hours.ago    def someDate = new Date()(2)    println someDate - 3.months}
1TimeCategory adds methods toInteger
2TimeCategory adds methods toDate

Theuse method takes the category class as its first parameter and a closure code block as second parameter. Inside theClosure access to the category methods is available. As can be seen in the example above even JDK classeslikejava.lang.Integer orjava.util.Date can be enriched with user-defined methods.

A category needs not to be directly exposed to the user code, the following will also do:

class JPACategory{  // Let's enhance JPA EntityManager without getting into the JSR committee  static void persistAll(EntityManager em , Object[] entities) { //add an interface to save all    entities?.each { em.persist(it) }  }}def transactionContext = {  EntityManager em, Closure c ->  def tx = em.transaction  try {    tx.begin()    use(JPACategory) {      c()    }    tx.commit()  } catch (e) {    tx.rollback()  } finally {    //cleanup your resource here  }}// user code, they always forget to close resource in exception, some even forget to commit, let's not rely on them.EntityManager em; //probably injectedtransactionContext (em) { em.persistAll(obj1, obj2, obj3) // let's do some logics here to make the example sensible em.persistAll(obj2, obj4, obj6)}

When we have a look at thegroovy.time.TimeCategory class we see that the extension methods are all declared asstaticmethods. In fact, this is one of the requirements that must be met by category classes for its methods to be successfully added toa class inside theuse code block:

public class TimeCategory {    public static Date plus(final Date date, final BaseDuration duration) {        return duration.plus(date);    }    public static Date minus(final Date date, final BaseDuration duration) {        final Calendar cal = Calendar.getInstance();        cal.setTime(date);        cal.add(Calendar.YEAR, -duration.getYears());        cal.add(Calendar.MONTH, -duration.getMonths());        cal.add(Calendar.DAY_OF_YEAR, -duration.getDays());        cal.add(Calendar.HOUR_OF_DAY, -duration.getHours());        cal.add(Calendar.MINUTE, -duration.getMinutes());        cal.add(Calendar.SECOND, -duration.getSeconds());        cal.add(Calendar.MILLISECOND, -duration.getMillis());        return cal.getTime();    }    // ...

Another requirement is the first argument of the static method must define the type the method is attached to once being activated. Theother arguments are the normal arguments the method will take as parameters.

Because of the parameter and static method convention, category method definitions may be a bit less intuitive thannormal method definitions. As an alternative Groovy comes with a@Category annotation that transforms annotated classesinto category classes at compile-time.

class Distance {    def number    String toString() { "${number}m" }}@Category(Number)class NumberCategory {    Distance getMeters() {        new Distance(number: this)    }}use (NumberCategory)  {    assert 42.meters.toString() == '42m'}

Applying the@Category annotation has the advantage of being able to use instance methods without the target type as afirst parameter. The target type class is given as an argument to the annotation instead.

There is a distinct section on@Category in thecompile-time metaprogramming section.
Metaclasses

As explained earlier, Metaclasses play a central role in method resolution.For every method invocation from groovy code, Groovy will find theMetaClass for the given objectand delegate the method resolution to the metaclass viagroovy.lang.MetaClass#invokeMethod(java.lang.Class,java.lang.Object,java.lang.String,java.lang.Object,boolean,boolean)which should not be confused withgroovy.lang.GroovyObject#invokeMethod(java.lang.String,java.lang.Object)which happens to be a method that the metaclass may eventually call.

The default metaclassMetaClassImpl

By default, objects get an instance ofMetaClassImpl that implements the default method lookup.This method lookup includes looking up of the method in the object class ("regular" method) but also if nomethod is found this way it will resort to callingmethodMissing and ultimatelygroovy.lang.GroovyObject#invokeMethod(java.lang.String,java.lang.Object)

class Foo {}def f = new Foo()assert f.metaClass =~ /MetaClassImpl/
Custom metaclasses

You can change the metaclass of any object or class and replace it with acustom implementation of theMetaClassgroovy.lang.MetaClass.Usually you will want to extend one of the existing metaclasses such asMetaClassImpl,DelegatingMetaClass,ExpandoMetaClass, orProxyMetaClass;otherwise you will need to implement the complete method lookup logic.Before using a new metaclass instance you should callgroovy.lang.MetaClass#initialize(),otherwise the metaclass may or may not behave as expected.

Delegating metaclass

If you only need to decorate an existing metaclass theDelegatingMetaClass simplifies that use case.The old metaclass implementation is still accessible viasuper making it easy to applypretransformations to the inputs, routing to other methods and postprocessing the outputs.

class Foo { def bar() { "bar" } }class MyFooMetaClass extends DelegatingMetaClass {  MyFooMetaClass(MetaClass metaClass) { super(metaClass) }  MyFooMetaClass(Class theClass) { super(theClass) }  Object invokeMethod(Object object, String methodName, Object[] args) {     def result = super.invokeMethod(object,methodName.toLowerCase(), args)     result.toUpperCase();  }}def mc =  new MyFooMetaClass(Foo.metaClass)mc.initialize()Foo.metaClass = mcdef f = new Foo()assert f.BAR() == "BAR" // the new metaclass routes .BAR() to .bar() and uppercases the result
Magic package

It is possible to change the metaclass at startup time by giving the metaclass a specially crafted (magic) class name and package name. In order to change the metaclass forjava.lang.Integer it’s enough to put a classgroovy.runtime.metaclass.java.lang.IntegerMetaClass in the classpath. This is useful, for example, when working with frameworks if you want to do metaclass changes before your code is executed by the framework. The general form of the magic package isgroovy.runtime.metaclass.[package].[class]MetaClass. In the example below the[package] isjava.lang and the[class] isInteger:

// file: IntegerMetaClass.groovypackage groovy.runtime.metaclass.java.lang;class IntegerMetaClass extends DelegatingMetaClass {  IntegerMetaClass(MetaClass metaClass) { super(metaClass) }  IntegerMetaClass(Class theClass) { super(theClass) }  Object invokeMethod(Object object, String name, Object[] args) {    if (name =~ /isBiggerThan/) {      def other = name.split(/isBiggerThan/)[1].toInteger()      object > other    } else {      return super.invokeMethod(object,name, args);    }  }}

By compiling the above file withgroovyc IntegerMetaClass.groovy a./groovy/runtime/metaclass/java/lang/IntegerMetaClass.class will be generated. The example below will use this new metaclass:

// File testInteger.groovydef i = 10assert i.isBiggerThan5()assert !i.isBiggerThan15()println i.isBiggerThan5()

By running that file withgroovy -cp . testInteger.groovy theIntegerMetaClass will be in the classpath and therefore it will become the metaclass forjava.lang.Integer intercepting the method calls toisBiggerThan*() methods.

Per instance metaclass

You can change the metaclass of individual objects separately, so it’s possible to have multiple object of the same class with different metaclasses.

class Foo { def bar() { "bar" }}class FooMetaClass extends DelegatingMetaClass {  FooMetaClass(MetaClass metaClass) { super(metaClass) }  Object invokeMethod(Object object, String name, Object[] args) {      super.invokeMethod(object,name,args).toUpperCase()  }}def f1 = new Foo()def f2 = new Foo()f2.metaClass = new FooMetaClass(f2.metaClass)assert f1.bar() == "bar"assert f2.bar() == "BAR"assert f1.metaClass =~ /MetaClassImpl/assert f2.metaClass =~ /FooMetaClass/assert f1.class.toString() == "class Foo"assert f2.class.toString() == "class Foo"
ExpandoMetaClass

Groovy comes with a specialMetaClass the so-calledExpandoMetaClass. It is special in that it allows for dynamicallyadding or changing methods, constructors, properties and even static methods by using a neat closure syntax.

Applying those modifications can be especially useful in mocking or stubbing scenarios as shown in theTesting Guide.

Everyjava.lang.Class is supplied by Groovy with a specialmetaClass property that will give you a reference to anExpandoMetaClass instance. This instance can then be used to add methods or change the behaviour of already existingones.

By defaultExpandoMetaClass doesn’t do inheritance. To enable this you must callExpandoMetaClass#enableGlobally()before your app starts such as in the main method or servlet bootstrap.

The following sections go into detail on howExpandoMetaClass can be used in various scenarios.

Methods

Once theExpandoMetaClass is accessed by calling themetaClass property, methods can be added by using either the left shift<< or the= operator.

Note that the left shift operator is used toappend a new method. If a public method with the same name andparameter types is declared by the class or interface, including those inherited from superclasses and superinterfacesbut excluding those added to themetaClass at runtime, an exception will be thrown. If you want toreplace amethod declared by the class or interface you can use the= operator.

The operators are applied on a non-existent property ofmetaClass passing an instance of aClosure code block.

class Book {   String title}Book.metaClass.titleInUpperCase << {-> title.toUpperCase() }def b = new Book(title:"The Stand")assert "THE STAND" == b.titleInUpperCase()

The example above shows how a new method can be added to a class by accessing themetaClass property and using the<< or= operator to assign aClosure code block. TheClosure parameters are interpreted as method parameters. Parameterless methods can be added by using the{→ …​} syntax.

Properties

ExpandoMetaClass supports two mechanisms for adding or overriding properties.

Firstly, it has support for declaring amutable property by simply assigning a value to a property ofmetaClass:

class Book {   String title}Book.metaClass.author = "Stephen King"def b = new Book()assert "Stephen King" == b.author

Another way is to add getter and/or setter methods by using the standard mechanisms for adding instance methods.

class Book {  String title}Book.metaClass.getAuthor << {-> "Stephen King" }def b = new Book()assert "Stephen King" == b.author

In the source code example above the property is dictated by the closure and is a read-only property. It is feasible to addan equivalent setter method but then the property value needs to be stored for later usage. This could be done asshown in the following example.

class Book {  String title}def properties = Collections.synchronizedMap([:])Book.metaClass.setAuthor = { String value ->   properties[System.identityHashCode(delegate) + "author"] = value}Book.metaClass.getAuthor = {->   properties[System.identityHashCode(delegate) + "author"]}

This is not the only technique however. For example in a servlet container one way might be to store the values inthe currently executing request as request attributes (as is done in some cases in Grails).

Constructors

Constructors can be added by using a specialconstructor property. Either the<< or= operator can be usedto assign aClosure code block. TheClosure arguments will become the constructor arguments when the code isexecuted at runtime.

class Book {    String title}Book.metaClass.constructor << { String title -> new Book(title:title) }def book = new Book('Groovy in Action - 2nd Edition')assert book.title == 'Groovy in Action - 2nd Edition'
Be careful when adding constructors however, as it is very easy to get into stack overflow troubles.
Static Methods

Static methods can be added using the same technique as instance methods with the addition of thestatic qualifierbefore the method name.

class Book {   String title}Book.metaClass.static.create << { String title -> new Book(title:title) }def b = Book.create("The Stand")
Borrowing Methods

WithExpandoMetaClass it is possible to use Groovy’s method pointer syntax to borrow methods from other classes.

class Person {    String name}class MortgageLender {   def borrowMoney() {      "buy house"   }}def lender = new MortgageLender()Person.metaClass.buyHouse = lender.&borrowMoneydef p = new Person()assert "buy house" == p.buyHouse()
Dynamic Method Names

Since Groovy allows you to use Strings as property names this in turns allows you to dynamically create method andproperty names at runtime. To create a method with a dynamic name simply use the language feature of reference propertynames as strings.

class Person {   String name = "Fred"}def methodName = "Bob"Person.metaClass."changeNameTo${methodName}" = {-> delegate.name = "Bob" }def p = new Person()assert "Fred" == p.namep.changeNameToBob()assert "Bob" == p.name

The same concept can be applied to static methods and properties.

One application of dynamic method names can be found in the Grails web application framework. The concept of "dynamiccodecs" is implemented by using dynamic method names.

HTMLCodec Class
class HTMLCodec {    static encode = { theTarget ->        HtmlUtils.htmlEscape(theTarget.toString())    }    static decode = { theTarget ->    HtmlUtils.htmlUnescape(theTarget.toString())    }}

The example above shows a codec implementation. Grails comes with various codec implementations each defined in a single class.At runtime there will be multiple codec classes in the application classpath. At application startup the framework addsaencodeXXX and adecodeXXX method to certain metaclasses whereXXX is the first part of the codec class name (e.g.encodeHTML). This mechanism is in the following shown in some Groovy pseudocode:

def codecs = classes.findAll { it.name.endsWith('Codec') }codecs.each { codec ->    Object.metaClass."encodeAs${codec.name-'Codec'}" = { codec.newInstance().encode(delegate) }    Object.metaClass."decodeFrom${codec.name-'Codec'}" = { codec.newInstance().decode(delegate) }}def html = '<html><body>hello</body></html>'assert '<html><body>hello</body></html>' == html.encodeAsHTML()
Runtime Discovery

At runtime it is often useful to know what other methods or properties exist at the time the method is executed.ExpandoMetaClassprovides the following methods as of this writing:

  • getMetaMethod

  • hasMetaMethod

  • getMetaProperty

  • hasMetaProperty

Why can’t you just use reflection? Well because Groovy is different, it has the methods that are "real" methods andmethods that are available only at runtime. These are sometimes (but not always) represented as MetaMethods. TheMetaMethods tell you what methods are available at runtime, thus your code can adapt.

This is of particular use when overridinginvokeMethod,getProperty and/orsetProperty.

GroovyObject Methods

Another feature ofExpandoMetaClass is that it allows to override the methodsinvokeMethod,getProperty andsetProperty, all of them can be found in thegroovy.lang.GroovyObject class.

The following example shows how to overrideinvokeMethod:

class Stuff {   def invokeMe() { "foo" }}Stuff.metaClass.invokeMethod = { String name, args ->   def metaMethod = Stuff.metaClass.getMetaMethod(name, args)   def result   if(metaMethod) result = metaMethod.invoke(delegate,args)   else {      result = "bar"   }   result}def stf = new Stuff()assert "foo" == stf.invokeMe()assert "bar" == stf.doStuff()

The first step in theClosure code is to look up theMetaMethod for the given name and arguments. If the methodcan be found everything is fine and it is delegated to. If not, a dummy value is returned.

AMetaMethod is a method that is known to exist on theMetaClass whether added at runtime or at compile-time.

The same logic can be used to overridesetProperty orgetProperty.

class Person {   String name = "Fred"}Person.metaClass.getProperty = { String name ->   def metaProperty = Person.metaClass.getMetaProperty(name)   def result   if(metaProperty) result = metaProperty.getProperty(delegate)   else {      result = "Flintstone"   }   result}def p = new Person()assert "Fred" == p.nameassert "Flintstone" == p.other

The important thing to note here is that instead of aMetaMethod aMetaProperty instance is looked up. If that existsthegetProperty method of theMetaProperty is called, passing the delegate.

Overriding Static invokeMethod

ExpandoMetaClass even allows for overriding static method with a specialinvokeMethod syntax.

class Stuff {   static invokeMe() { "foo" }}Stuff.metaClass.'static'.invokeMethod = { String name, args ->   def metaMethod = Stuff.metaClass.getStaticMetaMethod(name, args)   def result   if(metaMethod) result = metaMethod.invoke(delegate,args)   else {      result = "bar"   }   result}assert "foo" == Stuff.invokeMe()assert "bar" == Stuff.doStuff()

The logic that is used for overriding the static method is the same as we’ve seen before for overriding instance methods. Theonly difference is the access to themetaClass.static property and the call togetStaticMethodName for retrievingthe staticMetaMethod instance.

Extending Interfaces

It is possible to add methods onto interfaces withExpandoMetaClass. To do this however, itmust be enabledglobally using theExpandoMetaClass.enableGlobally() method before application start-up.

List.metaClass.sizeDoubled = {-> delegate.size() * 2 }def list = []list << 1list << 2assert 4 == list.sizeDoubled()
Extension modules
Extending existing classes

An extension module allows you to add new methods to existing classes, including classes which are precompiled, likeclasses from the JDK. Those new methods, unlike those defined through a metaclass or using a category, are availableglobally. For example, when you write:

Standard extension method
def file = new File(...)def contents = file.getText('utf-8')

ThegetText method doesn’t exist on theFile class. However, Groovy knows it because it is defined in a specialclass,ResourceGroovyMethods:

ResourceGroovyMethods.java
public static String getText(File file, String charset) throws IOException { return IOGroovyMethods.getText(newReader(file, charset));}

You may notice that the extension method is defined using a static method in a helper class (where various extensionmethods are defined). The first argument of thegetText method corresponds to the receiver, while additional parameterscorrespond to the arguments of the extension method. So here, we are defining a method calledgetText ontheFile class (because the first argument is of typeFile), which takes a single argument as a parameter (the encodingString).

The process of creating an extension module is simple:

  • write an extension class like above

  • write a module descriptor file

Then you have to make the extension module visible to Groovy, which is as simple as having the extension module classesand descriptor available on classpath. This means that you have the choice:

  • either provide the classes and module descriptor directly on classpath

  • or bundle your extension module into a jar for reusability

An extension module may add two kind of methods to a class:

  • instance methods (to be called on an instance of a class)

  • static methods (to be called on the class itself)

Instance methods

To add an instance method to an existing class, you need to create an extension class. For example, let’s say youwant to add amaxRetries method onInteger which accepts a closure and executes it at mostn times until noexception is thrown. To do that, you only need to write the following:

MaxRetriesExtension.groovy
class MaxRetriesExtension {(1)    static void maxRetries(Integer self, Closure code) {(2)        assert self >= 0        int retries = self        Throwable e = null        while (retries > 0) {            try {                code.call()                break            } catch (Throwable err) {                e = err                retries--            }        }        if (retries == 0 && e) {            throw e        }    }}
1The extension class
2First argument of the static method corresponds to the receiver of the message, that is to say the extended instance

Then, afterhaving declared your extension class, you can call it this way:

int i=05.maxRetries {    i++}assert i == 1i=0try {    5.maxRetries {        i++        throw new RuntimeException("oops")    }} catch (RuntimeException e) {    assert i == 5}
Static methods

It is also possible to add static methods to a class. In that case, the static method needs to be defined in itsownfile. Static and instance extension methodscannot be present in the same class.

StaticStringExtension.groovy
class StaticStringExtension {(1)    static String greeting(String self) {(2)        'Hello, world!'    }}
1The static extension class
2First argument of the static method corresponds to the class being extended and isunused

In which case you can call it directly on theString class:

assert String.greeting() == 'Hello, world!'
Module descriptor

For Groovy to be able to load your extension methods, you must declareyour extension helper classes. You must create a file namedorg.codehaus.groovy.runtime.ExtensionModule into theMETA-INF/groovy directory:

org.codehaus.groovy.runtime.ExtensionModule
moduleName=Test module for specificationsmoduleVersion=1.0-testextensionClasses=support.MaxRetriesExtensionstaticExtensionClasses=support.StaticStringExtension

The module descriptor requires 4 keys:

  • moduleName : the name of your module

  • moduleVersion: the version of your module. Note that version numberis only used to check that you don’t load the same module in twodifferent versions.

  • extensionClasses: the list of extension helper classes for instancemethods. You can provide several classes, given that they are commaseparated.

  • staticExtensionClasses: the list of extension helper classes forstatic methods. You can provide several classes, given that they arecomma separated.

Note that it is not required for a module to define both static helpersand instance helpers, and that you may add several classes to a singlemodule. You can also extend different classes in a single module withoutproblem. It is even possible to use different classes in a singleextension class, but it is recommended to group extension methods intoclasses by feature set.

Extension modules and classpath

It’s worth noting that you can’t use an extension which is compiled at the same time as code using it. That means thatto use an extension, ithas to be available on classpath, as compiled classes, before the code using it gets compiled.Usually, this means that you can’t have thetest classes in the same source unit as the extension class itself. Sincein general, test sources are separated from normal sources and executed in another step of the build, this is not an issue.

Compatibility with type checking

Unlike categories, extension modules are compatible with type checking: if they are found on classpath, then the typechecker is aware of the extension methods and will not complain when you call them. It is also compatible with staticcompilation.

3.4.2. Compile-time metaprogramming

Compile-time metaprogramming in Groovy allows code generation at compile-time. Those transformations are altering theAbstract Syntax Tree (AST) of a program, which is why in Groovy we call it AST transformations. AST transformationsallow you to hook into the compilation process, modify the AST and continue the compilation process to generate regularbytecode. Compared to runtime metaprogramming, this has the advantage of making the changes visible in the class fileitself (that is to say, in the bytecode). Making it visible in the bytecode is important for example if you want thetransformations to be part of the class contract (implementing interfaces, extending abstract classes, …​) or evenif you need your class to be callable from Java (or other JVM languages). For example, an AST transformation can addmethods to a class. If you do it with runtime metaprogramming, the new method would only be visible from Groovy. If youdo the same using compile-time metaprogramming, the method would be visible from Java too. Last but not least, performancewould likely be better with compile-time metaprogramming (because no initialization phase is required).

In this section, we will start with explaining the various compile-time transformations that are bundled with the Groovydistribution. In a subsequent section, we will describe how you canimplement your own AST transformationsand what are the disadvantages of this technique.

Available AST transformations

Groovy comes with various AST transformations covering different needs: reducing boilerplate (code generation), implementingdesign patterns (delegation, …​), logging, declarative concurrency, cloning, safer scripting, tweaking the compilation,implementing Swing patterns, testing and eventually managing dependencies. If none of those AST transformations coveryour needs, you can still implement your own, as show in sectionDeveloping your own AST transformations.

AST transformations can be separated into two categories:

  • global AST transformations are applied transparently, globally, as soon as they are found on compile classpath

  • local AST transformations are applied by annotating the source code with markers. Unlike global AST transformations,local AST transformations may support parameters.

Groovy doesn’t ship with any global AST transformation, but you can find a list of local AST transformationsavailable for you to use in your code here:

Code generation transformations

This category of transformation includes AST transformations which help removing boilerplate code. This is typicallycode that you have to write but that does not carry any useful information. By autogenerating this boilerplate code,the code you have to write is left clean and concise and the chance of introducing an error by getting suchboilerplate code incorrect is reduced.

@groovy.transform.ToString

The@ToString AST transformation generates a human-readabletoString representation of the class. For example,annotating thePerson class like below will automatically generate thetoString method for you:

import groovy.transform.ToString@ToStringclass Person {    String firstName    String lastName}

With this definition, then the following assertion passes, meaning that atoString method taking the field values fromthe class and printing them out has been generated:

def p = new Person(firstName: 'Jack', lastName: 'Nicholson')assert p.toString() == 'Person(Jack, Nicholson)'

The@ToString annotation accepts several parameters which are summarized in the following table:

AttributeDefault valueDescriptionExample

excludes

Empty list

List of properties to exclude from toString

@ToString(excludes=['firstName'])class Person {    String firstName    String lastName}def p = new Person(firstName: 'Jack', lastName: 'Nicholson')assert p.toString() == 'Person(Nicholson)'

includes

Undefined marker list (indicates all fields)

List of fields to include in toString

@ToString(includes=['lastName'])class Person {    String firstName    String lastName}def p = new Person(firstName: 'Jack', lastName: 'Nicholson')assert p.toString() == 'Person(Nicholson)'

includeSuper

False

Should superclass be included in toString

@ToStringclass Id { long id }@ToString(includeSuper=true)class Person extends Id {    String firstName    String lastName}def p = new Person(id:1, firstName: 'Jack', lastName: 'Nicholson')assert p.toString() == 'Person(Jack, Nicholson, Id(1))'

includeNames

false

Whether to include names of properties in generated toString.

@ToString(includeNames=true)class Person {    String firstName    String lastName}def p = new Person(firstName: 'Jack', lastName: 'Nicholson')assert p.toString() == 'Person(firstName:Jack, lastName:Nicholson)'

includeFields

False

Should fields be included in toString, in addition to properties

@ToString(includeFields=true)class Person {    String firstName    String lastName    private int age    void test() {       age = 42    }}def p = new Person(firstName: 'Jack', lastName: 'Nicholson')p.test()assert p.toString() == 'Person(Jack, Nicholson, 42)'

includeSuperProperties

False

Should super properties be included in toString

class Person {    String name}@ToString(includeSuperProperties = true, includeNames = true)class BandMember extends Person {    String bandName}def bono = new BandMember(name:'Bono', bandName: 'U2').toString()assert bono.toString() == 'BandMember(bandName:U2, name:Bono)'

includeSuperFields

False

Should visible super fields be included in toString

class Person {    protected String name}@ToString(includeSuperFields = true, includeNames = true)@MapConstructor(includeSuperFields = true)class BandMember extends Person {    String bandName}def bono = new BandMember(name:'Bono', bandName: 'U2').toString()assert bono.toString() == 'BandMember(bandName:U2, name:Bono)'

ignoreNulls

False

Should properties/fields with null value be displayed

@ToString(ignoreNulls=true)class Person {    String firstName    String lastName}def p = new Person(firstName: 'Jack')assert p.toString() == 'Person(Jack)'

includePackage

True

Use fully qualified class name instead of simple name in toString

@ToString(includePackage=true)class Person {    String firstName    String lastName}def p = new Person(firstName: 'Jack', lastName:'Nicholson')assert p.toString() == 'acme.Person(Jack, Nicholson)'

allProperties

True

Include all JavaBean properties in toString

@ToString(includeNames=true)class Person {    String firstName    String getLastName() { 'Nicholson' }}def p = new Person(firstName: 'Jack')assert p.toString() == 'acme.Person(firstName:Jack, lastName:Nicholson)'

cache

False

Cache the toString string. Should only be set to true if the class is immutable.

@ToString(cache=true)class Person {    String firstName    String lastName}def p = new Person(firstName: 'Jack', lastName:'Nicholson')def s1 = p.toString()def s2 = p.toString()assert s1 == s2assert s1 == 'Person(Jack, Nicholson)'assert s1.is(s2) // same instance

allNames

False

Should fields and/or properties with internal names be included in the generated toString

@ToString(allNames=true)class Person {    String $firstName}def p = new Person($firstName: "Jack")assert p.toString() == 'acme.Person(Jack)'
@groovy.transform.EqualsAndHashCode

The@EqualsAndHashCode AST transformation aims at generatingequals andhashCode methods for you. The generatedhashcode follows the best practices as described inEffective Java byJosh Bloch:

import groovy.transform.EqualsAndHashCode@EqualsAndHashCodeclass Person {    String firstName    String lastName}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')def p2 = new Person(firstName: 'Jack', lastName: 'Nicholson')assert p1==p2assert p1.hashCode() == p2.hashCode()

There are several options available to tweak the behavior of@EqualsAndHashCode:

AttributeDefault valueDescriptionExample

excludes

Empty list

List of properties to exclude from equals/hashCode

import groovy.transform.EqualsAndHashCode@EqualsAndHashCode(excludes=['firstName'])class Person {    String firstName    String lastName}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')def p2 = new Person(firstName: 'Bob', lastName: 'Nicholson')assert p1==p2assert p1.hashCode() == p2.hashCode()

includes

Undefined marker list (indicating all fields)

List of fields to include in equals/hashCode

import groovy.transform.EqualsAndHashCode@EqualsAndHashCode(includes=['lastName'])class Person {    String firstName    String lastName}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')def p2 = new Person(firstName: 'Bob', lastName: 'Nicholson')assert p1==p2assert p1.hashCode() == p2.hashCode()

cache

False

Cache the hashCode computation. Should only be set to true if the class is immutable.

import groovy.transform.EqualsAndHashCodeimport groovy.transform.Immutable@Immutableclass SlowHashCode {    static final SLEEP_PERIOD = 500    int hashCode() {        sleep SLEEP_PERIOD        127    }}@EqualsAndHashCode(cache=true)@Immutableclass Person {    SlowHashCode slowHashCode = new SlowHashCode()}def p = new Person()p.hashCode()def start = System.currentTimeMillis()p.hashCode()assert System.currentTimeMillis() - start < SlowHashCode.SLEEP_PERIOD

callSuper

False

Whether to include super in equals and hashCode calculations

import groovy.transform.EqualsAndHashCode@EqualsAndHashCodeclass Living {    String race}@EqualsAndHashCode(callSuper=true)class Person extends Living {    String firstName    String lastName}def p1 = new Person(race:'Human', firstName: 'Jack', lastName: 'Nicholson')def p2 = new Person(race: 'Human being', firstName: 'Jack', lastName: 'Nicholson')assert p1!=p2assert p1.hashCode() != p2.hashCode()

includeFields

False

Should fields be included in equals/hashCode, in addition to properties

import groovy.transform.EqualsAndHashCode@EqualsAndHashCode(includeFields=true)class Person {    private String firstName    Person(String firstName) {        this.firstName = firstName    }}def p1 = new Person('Jack')def p2 = new Person('Jack')def p3 = new Person('Bob')assert p1 == p2assert p1 != p3

useCanEqual

True

Should equals call canEqual helper method.

allProperties

False

Should JavaBean properties be included in equals and hashCode calculations

@EqualsAndHashCode(allProperties=true, excludes='first, last')class Person {    String first, last    String getInitials() { first[0] + last[0] }}def p1 = new Person(first: 'Jack', last: 'Smith')def p2 = new Person(first: 'Jack', last: 'Spratt')def p3 = new Person(first: 'Bob', last: 'Smith')assert p1 == p2assert p1.hashCode() == p2.hashCode()assert p1 != p3assert p1.hashCode() != p3.hashCode()

allNames

False

Should fields and/or properties with internal names be included in equals and hashCode calculations

import groovy.transform.EqualsAndHashCode@EqualsAndHashCode(allNames=true)class Person {    String $firstName}def p1 = new Person($firstName: 'Jack')def p2 = new Person($firstName: 'Bob')assert p1 != p2assert p1.hashCode() != p2.hashCode()
@groovy.transform.TupleConstructor

The@TupleConstructor annotation aims at eliminating boilerplate code by generating constructors for you. A tupleconstructor is created having a parameter for each property (and possibly each field). Each parameter has a default value(using the initial value of the property if present or otherwise Java’s default value according to the properties type).

Implementation Details

Normally you don’t need to understand the implementation details of the generated constructor(s); you just use them in the normal way.However, if you want to add multiple constructors, understand Java integration options or meet requirements of somedependency injection frameworks, then some details are useful.

As previously mentioned, the generated constructor has default values applied. In later compilation phases,the Groovy compiler’s standard default value processing behavior is then applied.The end result is that multiple constructors are placed within the bytecode of your class.This provides a well understood semantics and is also useful for Java integration purposes. As an example, thefollowing code will generate 3 constructors:

import groovy.transform.TupleConstructor@TupleConstructorclass Person {    String firstName    String lastName}// traditional map-style constructordef p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')// generated tuple constructordef p2 = new Person('Jack', 'Nicholson')// generated tuple constructor with default value for second propertydef p3 = new Person('Jack')

The first constructor is a no-arg constructor which allows the traditional map-style construction so long asyou don’t have final properties. Groovy calls the no-arg constructor and then the relevant setters under the covers.It is worth noting that if the first property (or field) has type LinkedHashMap or if there is a single Map,AbstractMap or HashMap property (or field), then the map-style named arguments won’t be available.

The other constructors are generated by taking the properties in the order they are defined. Groovy will generate asmany constructors as there are properties (or fields, depending on the options).

Setting thedefaults attribute (see the available configuration options table) tofalse, disables the normal default values behavior which means:

  • Exactly one constructor will be produced

  • Attempting to use an initial value will produce an error

  • Map-style named arguments won’t be available

This attribute is normally only used in situations where another Java framework isexpecting exactly one constructor, e.g. injection frameworks or JUnit parameterized runners.

Immutability support

If the@PropertyOptions annotation is also found on the class with the@TupleConstructor annotation,then the generated constructor may contain custom property handling logic.ThepropertyHandler attribute on the@PropertyOptions annotation could for instance be set toImmutablePropertyHandler which will result in the addition of the necessary logic for immutable classes(defensive copy in, cloning, etc.). This normally would happen automatically behind the scenes when you usethe@Immutable meta-annotation.Some of the annotation attributes might not be supported by all property handlers.

Customization options

The@TupleConstructor AST transformation accepts several annotation attributes:

AttributeDefault valueDescriptionExample

excludes

Empty list

List of properties to exclude from tuple constructor generation

import groovy.transform.TupleConstructor@TupleConstructor(excludes=['lastName'])class Person {    String firstName    String lastName}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')def p2 = new Person('Jack')try {    // will fail because the second property is excluded    def p3 = new Person('Jack', 'Nicholson')} catch (e) {    assert e.message.contains ('Could not find matching constructor')}

includes

Undefined list (indicates all fields)

List of fields to include in tuple constructor generation

import groovy.transform.TupleConstructor@TupleConstructor(includes=['firstName'])class Person {    String firstName    String lastName}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')def p2 = new Person('Jack')try {    // will fail because the second property is not included    def p3 = new Person('Jack', 'Nicholson')} catch (e) {    assert e.message.contains ('Could not find matching constructor')}

includeProperties

True

Should properties be included in tuple constructor generation

import groovy.transform.TupleConstructor@TupleConstructor(includeProperties=false)class Person {    String firstName    String lastName}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')try {    def p2 = new Person('Jack', 'Nicholson')} catch(e) {    // will fail because properties are not included}

includeFields

False

Should fields be included in tuple constructor generation, in addition to properties

import groovy.transform.TupleConstructor@TupleConstructor(includeFields=true)class Person {    String firstName    String lastName    private String occupation    public String toString() {        "$firstName $lastName: $occupation"    }}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson', occupation: 'Actor')def p2 = new Person('Jack', 'Nicholson', 'Actor')assert p1.firstName == p2.firstNameassert p1.lastName == p2.lastNameassert p1.toString() == 'Jack Nicholson: Actor'assert p1.toString() == p2.toString()

includeSuperProperties

True

Should properties from super classes be included in tuple constructor generation

import groovy.transform.TupleConstructorclass Base {    String occupation}@TupleConstructor(includeSuperProperties=true)class Person extends Base {    String firstName    String lastName    public String toString() {        "$firstName $lastName: $occupation"    }}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')def p2 = new Person('Actor', 'Jack', 'Nicholson')assert p1.firstName == p2.firstNameassert p1.lastName == p2.lastNameassert p1.toString() == 'Jack Nicholson: null'assert p2.toString() == 'Jack Nicholson: Actor'

includeSuperFields

False

Should fields from super classes be included in tuple constructor generation

import groovy.transform.TupleConstructorclass Base {    protected String occupation    public String occupation() { this.occupation }}@TupleConstructor(includeSuperFields=true)class Person extends Base {    String firstName    String lastName    public String toString() {        "$firstName $lastName: ${occupation()}"    }}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson', occupation: 'Actor')def p2 = new Person('Actor', 'Jack', 'Nicholson')assert p1.firstName == p2.firstNameassert p1.lastName == p2.lastNameassert p1.toString() == 'Jack Nicholson: Actor'assert p2.toString() == p1.toString()

callSuper

False

Should super properties be called within a call to the parent constructor rather than set as properties

import groovy.transform.TupleConstructorclass Base {    String occupation    Base() {}    Base(String job) { occupation = job?.toLowerCase() }}@TupleConstructor(includeSuperProperties = true, callSuper=true)class Person extends Base {    String firstName    String lastName    public String toString() {        "$firstName $lastName: $occupation"    }}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')def p2 = new Person('ACTOR', 'Jack', 'Nicholson')assert p1.firstName == p2.firstNameassert p1.lastName == p2.lastNameassert p1.toString() == 'Jack Nicholson: null'assert p2.toString() == 'Jack Nicholson: actor'

force

False

By default, the transformation will do nothing if a constructor is already defined. Setting this attribute totrue, the constructor will be generated and it’s your responsibility to ensure that no duplicate constructor is defined.

import groovy.transform.*@ToString @TupleConstructor(force=true)final class Person {    String name    // explicit constructor would normally disable tuple constructor    Person(String first, String last) { this("$first $last") }}assert new Person('john smith').toString() == 'Person(john smith)'assert new Person('john', 'smith').toString() == 'Person(john smith)'

defaults

True

Indicates that default value processing is enabled for constructor parameters.Set to false to obtain exactly one constructor but with initial value support and named-arguments disabled.

@ToString@TupleConstructor(defaults=false)class Musician {  String name  String instrument  int born}assert new Musician('Jimi', 'Guitar', 1942).toString() == 'Musician(Jimi, Guitar, 1942)'assert Musician.constructors.size() == 1

useSetters

False

By default, the transformation will directly set the backing field of each propertyfrom its corresponding constructor parameter. Setting this attribute to true, the constructor will instead call setters ifthey exist. It’s usually deemed bad style from within a constructor to call setters that can be overridden. It’s yourresponsibility to avoid such bad style.

import groovy.transform.*@ToString @TupleConstructor(useSetters=true)final class Foo {    String bar    void setBar(String bar) {        this.bar = bar?.toUpperCase() // null-safe    }}assert new Foo('cat').toString() == 'Foo(CAT)'assert new Foo(bar: 'cat').toString() == 'Foo(CAT)'

allNames

False

Should fields and/or properties with internal names be included within the constructor

import groovy.transform.TupleConstructor@TupleConstructor(allNames=true)class Person {    String $firstName}def p = new Person('Jack')assert p.$firstName == 'Jack'

allProperties

False

Should JavaBean properties be included within the constructor

@TupleConstructor(allProperties=true)class Person {    String first    private String last    void setLast(String last) {        this.last = last    }    String getName() { "$first $last" }}assert new Person('john', 'smith').name == 'john smith'

pre

empty

A closure containing statements to be inserted at the start of the generated constructor(s)

import groovy.transform.TupleConstructor@TupleConstructor(pre={ first = first?.toLowerCase() })class Person {    String first}def p = new Person('Jack')assert p.first == 'jack'

post

empty

A closure containing statements to be inserted at the end of the generated constructor(s)

import groovy.transform.TupleConstructorimport static groovy.test.GroovyAssert.shouldFail@TupleConstructor(post={ assert first })class Person {    String first}def jack = new Person('Jack')shouldFail {  def unknown = new Person()}

Setting thedefaults annotation attribute tofalse and theforce annotation attribute totrue allowsmultiple tuple constructors to be created by using different customization options for the different cases(provided each case has a different type signature) as shown in the following example:

class Named {  String name}@ToString(includeSuperProperties=true, ignoreNulls=true, includeNames=true, includeFields=true)@TupleConstructor(force=true, defaults=false)@TupleConstructor(force=true, defaults=false, includeFields=true)@TupleConstructor(force=true, defaults=false, includeSuperProperties=true)class Book extends Named {  Integer published  private Boolean fiction  Book() {}}assert new Book("Regina", 2015).toString() == 'Book(published:2015, name:Regina)'assert new Book(2015, false).toString() == 'Book(published:2015, fiction:false)'assert new Book(2015).toString() == 'Book(published:2015)'assert new Book().toString() == 'Book()'assert Book.constructors.size() == 4

Similarly, here is another example using different options forincludes:

@ToString(includeSuperProperties=true, ignoreNulls=true, includeNames=true, includeFields=true)@TupleConstructor(force=true, defaults=false, includes='name,year')@TupleConstructor(force=true, defaults=false, includes='year,fiction')@TupleConstructor(force=true, defaults=false, includes='name,fiction')class Book {    String name    Integer year    Boolean fiction}assert new Book("Regina", 2015).toString() == 'Book(name:Regina, year:2015)'assert new Book(2015, false).toString() == 'Book(year:2015, fiction:false)'assert new Book("Regina", false).toString() == 'Book(name:Regina, fiction:false)'assert Book.constructors.size() == 3
@groovy.transform.MapConstructor

The@MapConstructor annotation aims at eliminating boilerplate code by generating a map constructor for you. A mapconstructor is created such that each property in the class is set based on the value in the supplied maphaving the key with the name of the property. Usage is as shown in this example:

import groovy.transform.*@ToString@MapConstructorclass Person {    String firstName    String lastName}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')assert p1.toString() == 'Person(Jack, Nicholson)'

The generated constructor will be roughly like this:

public Person(Map args) {    if (args.containsKey('firstName')) {        this.firstName = args.get('firstName')    }    if (args.containsKey('lastName')) {        this.lastName = args.get('lastName')    }}
@groovy.transform.Canonical

The@Canonical meta-annotation combines the@ToString,@EqualsAndHashCode and@TupleConstructorannotations:

import groovy.transform.Canonical@Canonicalclass Person {    String firstName    String lastName}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')assert p1.toString() == 'Person(Jack, Nicholson)' // Effect of @ToStringdef p2 = new Person('Jack','Nicholson') // Effect of @TupleConstructorassert p2.toString() == 'Person(Jack, Nicholson)'assert p1==p2 // Effect of @EqualsAndHashCodeassert p1.hashCode()==p2.hashCode() // Effect of @EqualsAndHashCode

A similar immutable class can be generated using the@Immutable meta-annotation instead.The@Canonical meta-annotation supports the configuration options found in the annotationsit aggregates. See those annotations for more details.

import groovy.transform.Canonical@Canonical(excludes=['lastName'])class Person {    String firstName    String lastName}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')assert p1.toString() == 'Person(Jack)' // Effect of @ToString(excludes=['lastName'])def p2 = new Person('Jack') // Effect of @TupleConstructor(excludes=['lastName'])assert p2.toString() == 'Person(Jack)'assert p1==p2 // Effect of @EqualsAndHashCode(excludes=['lastName'])assert p1.hashCode()==p2.hashCode() // Effect of @EqualsAndHashCode(excludes=['lastName'])

The@Canonical meta-annotation can be used in conjunction with an explicit use one or more of itscomponent annotations, like this:

import groovy.transform.Canonical@Canonical(excludes=['lastName'])class Person {    String firstName    String lastName}def p1 = new Person(firstName: 'Jack', lastName: 'Nicholson')assert p1.toString() == 'Person(Jack)' // Effect of @ToString(excludes=['lastName'])def p2 = new Person('Jack') // Effect of @TupleConstructor(excludes=['lastName'])assert p2.toString() == 'Person(Jack)'assert p1==p2 // Effect of @EqualsAndHashCode(excludes=['lastName'])assert p1.hashCode()==p2.hashCode() // Effect of @EqualsAndHashCode(excludes=['lastName'])

Any applicable annotation attributes from@Canonical are passed along to the explicit annotation butattributes already existing in the explicit annotation take precedence.

@groovy.transform.InheritConstructors

The@InheritConstructor AST transformation aims at generating constructors matching super constructors for you. Thisis in particular useful when overriding exception classes:

import groovy.transform.InheritConstructors@InheritConstructorsclass CustomException extends Exception {}// all those are generated constructorsnew CustomException()new CustomException("A custom message")new CustomException("A custom message", new RuntimeException())new CustomException(new RuntimeException())// Java 7 only// new CustomException("A custom message", new RuntimeException(), false, true)

The@InheritConstructor AST transformation supports the following configuration options:

AttributeDefault valueDescriptionExample

constructorAnnotations

False

Whether to carry over annotations from the constructor during copying

@Retention(RetentionPolicy.RUNTIME)@Target([ElementType.CONSTRUCTOR])public @interface ConsAnno {}class Base {  @ConsAnno Base() {}}@InheritConstructors(constructorAnnotations=true)class Child extends Base {}assert Child.constructors[0].annotations[0].annotationType().name == 'groovy.transform.Generated'assert Child.constructors[0].annotations[1].annotationType().name == 'ConsAnno'

parameterAnnotations

False

Whether to carry over annotations from the constructor parameters when copying the constructor

@Retention(RetentionPolicy.RUNTIME)@Target([ElementType.PARAMETER])public @interface ParamAnno {}class Base {  Base(@ParamAnno String name) {}}@InheritConstructors(parameterAnnotations=true)class Child extends Base {}assert Child.constructors[0].parameterAnnotations[0][0].annotationType().name == 'ParamAnno'
@groovy.lang.Category

The@Category AST transformation simplifies the creation of Groovy categories. Historically, a Groovy category waswritten like this:

class TripleCategory {    public static Integer triple(Integer self) {        3*self    }}use (TripleCategory) {    assert 9 == 3.triple()}

The@Category transformation lets you write the same using an instance-style class, rather than a static class style.This removes the need for having the first argument of each method being the receiver. The category can be written likethis:

@Category(Integer)class TripleCategory {    public Integer triple() { 3*this }}use (TripleCategory) {    assert 9 == 3.triple()}

Note that the mixed in class can be referenced usingthis instead. It’s also worth noting that using instance fieldsin a category class is inherently unsafe: categories are not stateful (like traits).

@groovy.transform.IndexedProperty

The@IndexedProperty annotation aims at generating indexed getters/setters for properties of list/array types.This is in particular useful if you want to use a Groovy class from Java. While Groovy supports GPath to access properties,this is not available from Java. The@IndexedProperty annotation will generate indexed properties of the followingform:

class SomeBean {    @IndexedProperty String[] someArray = new String[2]    @IndexedProperty List someList = []}def bean = new SomeBean()bean.setSomeArray(0, 'value')bean.setSomeList(0, 123)assert bean.someArray[0] == 'value'assert bean.someList == [123]
@groovy.lang.Lazy

The@Lazy AST transformation implements lazy initialization of fields. For example, the following code:

class SomeBean {    @Lazy LinkedList myField}

will produce the following code:

List $myFieldList getMyField() {    if ($myField!=null) { return $myField }    else {        $myField = new LinkedList()        return $myField    }}

The default value which is used to initialize the field is the default constructor of the declaration type. It is possibleto define a default value by using a closure on the right hand side of the property assignment, as in the followingexample:

class SomeBean {    @Lazy LinkedList myField = { ['a','b','c']}()}

In that case, the generated code looks like the following:

List $myFieldList getMyField() {    if ($myField!=null) { return $myField }    else {        $myField = { ['a','b','c']}()        return $myField    }}

If the field is declared volatile then initialization will be synchronized using thedouble-checked locking pattern.

Using thesoft=true parameter, the helper field will use aSoftReference instead, providing a simple way toimplement caching. In that case, if the garbage collector decides to collect the reference, initialization will occurthe next time the field is accessed.

@groovy.lang.Newify

The@Newify AST transformation is used to bring alternative syntaxes to construct objects:

  • Using thePython style:

@Newify([Tree,Leaf])class TreeBuilder {    Tree tree = Tree(Leaf('A'),Leaf('B'),Tree(Leaf('C')))}
  • or using theRuby style:

@Newify([Tree,Leaf])class TreeBuilder {    Tree tree = Tree.new(Leaf.new('A'),Leaf.new('B'),Tree.new(Leaf.new('C')))}

TheRuby version can be disabled by setting theauto flag tofalse.

@groovy.transform.Sortable

The@Sortable AST transformation is used to help write classes that areComparable and easily sortedtypically by numerous properties. It is easy to use as shown in the following example where we annotatethePerson class:

import groovy.transform.Sortable@Sortable class Person {    String first    String last    Integer born}

The generated class has the following properties:

  • it implements theComparable interface

  • it contains acompareTo method with an implementation based on the natural ordering of thefirst,last andborn properties

  • it has three methods returning comparators:comparatorByFirst,comparatorByLast andcomparatorByBorn.

The generatedcompareTo method will look like this:

public int compareTo(java.lang.Object obj) {    if (this.is(obj)) {        return 0    }    if (!(obj instanceof Person)) {        return -1    }    java.lang.Integer value = this.first <=> obj.first    if (value != 0) {        return value    }    value = this.last <=> obj.last    if (value != 0) {        return value    }    value = this.born <=> obj.born    if (value != 0) {        return value    }    return 0}

As an example of the generated comparators, thecomparatorByFirst comparator will have acompare method that looks like this:

public int compare(java.lang.Object arg0, java.lang.Object arg1) {    if (arg0 == arg1) {        return 0    }    if (arg0 != null && arg1 == null) {        return -1    }    if (arg0 == null && arg1 != null) {        return 1    }    return arg0.first <=> arg1.first}

ThePerson class can be used wherever aComparable is expected and the generated comparatorswherever aComparator is expected as shown by these examples:

def people = [    new Person(first: 'Johnny', last: 'Depp', born: 1963),    new Person(first: 'Keira', last: 'Knightley', born: 1985),    new Person(first: 'Geoffrey', last: 'Rush', born: 1951),    new Person(first: 'Orlando', last: 'Bloom', born: 1977)]assert people[0] > people[2]assert people.sort()*.last == ['Rush', 'Depp', 'Knightley', 'Bloom']assert people.sort(false, Person.comparatorByFirst())*.first == ['Geoffrey', 'Johnny', 'Keira', 'Orlando']assert people.sort(false, Person.comparatorByLast())*.last == ['Bloom', 'Depp', 'Knightley', 'Rush']assert people.sort(false, Person.comparatorByBorn())*.last == ['Rush', 'Depp', 'Bloom', 'Knightley']

Normally, all properties are used in the generatedcompareTo method in the priority order in which they are defined.You can include or exclude certain properties from the generatedcompareTo method by giving a list of property namesin theincludes orexcludes annotation attributes. If usingincludes, the order of the property names given willdetermine the priority of properties when comparing. To illustrate, consider the followingPerson class definition:

@Sortable(includes='first,born') class Person {    String last    int born    String first}

It will have two comparator methodscomparatorByFirst andcomparatorByBorn and the generatedcompareTo method will look like this:

public int compareTo(java.lang.Object obj) {    if (this.is(obj)) {        return 0    }    if (!(obj instanceof Person)) {        return -1    }    java.lang.Integer value = this.first <=> obj.first    if (value != 0) {        return value    }    value = this.born <=> obj.born    if (value != 0) {        return value    }    return 0}

ThisPerson class can be used as follows:

def people = [    new Person(first: 'Ben', last: 'Affleck', born: 1972),    new Person(first: 'Ben', last: 'Stiller', born: 1965)]assert people.sort()*.last == ['Stiller', 'Affleck']

The behavior of the@Sortable AST transformation can be further changed using the following additional parameters:

AttributeDefault valueDescriptionExample

allProperties

True

Should JavaBean properties (ordered after native properties) be used

import groovy.transform.*@Canonical(includeFields = true)@Sortable(allProperties = true, includes = 'nameSize')class Player {  String name  int getNameSize() { name.size() }}def finalists = [  new Player('Serena'),  new Player('Venus'),  new Player('CoCo'),  new Player('Mirjana')]assert finalists.sort()*.name == ['CoCo', 'Venus', 'Serena', 'Mirjana']

allNames

False

Should properties with "internal" names be used

import groovy.transform.*@Canonical(allNames = true)@Sortable(allNames = false)class Player {  String $country  String name}def finalists = [  new Player('USA', 'Serena'),  new Player('USA', 'Venus'),  new Player('USA', 'CoCo'),  new Player('Croatian', 'Mirjana')]assert finalists.sort()*.name == ['Mirjana', 'CoCo', 'Serena', 'Venus']

includeSuperProperties

False

Should super properties also be used (ordered first)

class Person {  String name}@Canonical(includeSuperProperties = true)@Sortable(includeSuperProperties = true)class Citizen extends Person {  String country}def people = [  new Citizen('Bob', 'Italy'),  new Citizen('Cathy', 'Hungary'),  new Citizen('Cathy', 'Egypt'),  new Citizen('Bob', 'Germany'),  new Citizen('Alan', 'France')]assert people.sort()*.name == ['Alan', 'Bob', 'Bob', 'Cathy', 'Cathy']assert people.sort()*.country == ['France', 'Germany', 'Italy', 'Egypt', 'Hungary']
@groovy.transform.builder.Builder

The@Builder AST transformation is used to help write classes that can be created usingfluent api calls.The transform supports multiple building strategies to cover a range of cases and there are a numberof configuration options to customize the building process. If you’re an AST hacker, you can also define your ownstrategy class. The following table lists the available strategies that are bundled with Groovy and theconfiguration options each strategy supports.

Strategy

Description

builderClassName

builderMethodName

buildMethodName

prefix

includes/excludes

includeSuperProperties

allNames

SimpleStrategy

chained setters

n/a

n/a

n/a

yes, default "set"

yes

n/a

yes, defaultfalse

ExternalStrategy

explicit builder class, class being built untouched

n/a

n/a

yes, default "build"

yes, default ""

yes

yes, defaultfalse

yes, defaultfalse

DefaultStrategy

creates a nested helper class

yes, default<TypeName>Builder

yes, default "builder"

yes, default "build"

yes, default ""

yes

yes, defaultfalse

yes, defaultfalse

InitializerStrategy

creates a nested helper class providing type-safe fluent creation

yes, default<TypeName>Initializer

yes, default "createInitializer"

yes, default "create" but usually only used internally

yes, default ""

yes

yes, defaultfalse

yes, defaultfalse

SimpleStrategy

To use theSimpleStrategy, annotate your Groovy class using the@Builder annotation, and specify the strategy as shown in this example:

import groovy.transform.builder.*@Builder(builderStrategy=SimpleStrategy)class Person {    String first    String last    Integer born}

Then, just call the setters in a chained fashion as shown here:

def p1 = new Person().setFirst('Johnny').setLast('Depp').setBorn(1963)assert "$p1.first $p1.last" == 'Johnny Depp'

For each property, a generated setter will be created which looks like this:

public Person setFirst(java.lang.String first) {    this.first = first    return this}

You can specify a prefix as shown in this example:

import groovy.transform.builder.*@Builder(builderStrategy=SimpleStrategy, prefix="")class Person {    String first    String last    Integer born}

And calling the chained setters would look like this:

def p = new Person().first('Johnny').last('Depp').born(1963)assert "$p.first $p.last" == 'Johnny Depp'

You can use theSimpleStrategy in conjunction with@TupleConstructor. If your@Builderannotation doesn’t have explicitincludes orexcludes annotation attributes but your@TupleConstructorannotation does, the ones from@TupleConstructor will be re-used for@Builder. The same applies for anyannotation aliases which combine@TupleConstructor such as@Canonical.

The annotation attributeuseSetters can be used if you have a setter which you want called as part of theconstruction process. See the JavaDoc for details.

The annotation attributesbuilderClassName,buildMethodName,builderMethodName,forClass andincludeSuperProperties are not supported for this strategy.

Groovy already has built-in building mechanisms. Don’t rush to using@Builder if the built-in mechanisms meet your needs. Some examples:
def p2 = new Person(first: 'Keira', last: 'Knightley', born: 1985)def p3 = new Person().with {    first = 'Geoffrey'    last = 'Rush'    born = 1951}
ExternalStrategy

To use theExternalStrategy, create and annotate a Groovy builder class using the@Builder annotation, specify theclass the builder is for usingforClass and indicate use of theExternalStrategy.Suppose you have the following class you would like a builder for:

class Person {    String first    String last    int born}

you explicitly create and use your builder class as follows:

import groovy.transform.builder.*@Builder(builderStrategy=ExternalStrategy, forClass=Person)class PersonBuilder { }def p = new PersonBuilder().first('Johnny').last('Depp').born(1963).build()assert "$p.first $p.last" == 'Johnny Depp'

Note that the (normally empty) builder class you provide will be filled in with appropriate setters and a build method.The generated build method will look something like:

public Person build() {    Person _thePerson = new Person()    _thePerson.first = first    _thePerson.last = last    _thePerson.born = born    return _thePerson}

The class you are creating the builder for can be any Java or Groovy class following the normal JavaBean conventions,e.g. a no-arg constructor and setters for the properties. Here is an example using a Java class:

import groovy.transform.builder.*@Builder(builderStrategy=ExternalStrategy, forClass=javax.swing.DefaultButtonModel)class ButtonModelBuilder {}def model = new ButtonModelBuilder().enabled(true).pressed(true).armed(true).rollover(true).selected(true).build()assert model.isArmed()assert model.isPressed()assert model.isEnabled()assert model.isSelected()assert model.isRollover()

The generated builder can be customised using theprefix,includes,excludes andbuildMethodName annotation attributes.Here is an example illustrating various customisations:

import groovy.transform.builder.*import groovy.transform.Canonical@Canonicalclass Person {    String first    String last    int born}@Builder(builderStrategy=ExternalStrategy, forClass=Person, includes=['first', 'last'], buildMethodName='create', prefix='with')class PersonBuilder { }def p = new PersonBuilder().withFirst('Johnny').withLast('Depp').create()assert "$p.first $p.last" == 'Johnny Depp'

ThebuilderMethodName andbuilderClassName annotation attributes for@Builder aren’t applicable for this strategy.

You can use theExternalStrategy in conjunction with@TupleConstructor. If your@Builder annotation doesn’t haveexplicitincludes orexcludes annotation attributes but the@TupleConstructor annotation of the class you are creatingthe builder for does, the ones from@TupleConstructor will be re-used for@Builder. The same applies for anyannotation aliases which combine@TupleConstructor such as@Canonical.

DefaultStrategy

To use theDefaultStrategy, annotate your Groovy class using the@Builder annotation as shown in this example:

import groovy.transform.builder.Builder@Builderclass Person {    String firstName    String lastName    int age}def person = Person.builder().firstName("Robert").lastName("Lewandowski").age(21).build()assert person.firstName == "Robert"assert person.lastName == "Lewandowski"assert person.age == 21

If you want, you can customize various aspects of the building processusing thebuilderClassName,buildMethodName,builderMethodName,prefix,includes andexcludes annotation attributes,some of which are used in the example here:

import groovy.transform.builder.Builder@Builder(buildMethodName='make', builderMethodName='maker', prefix='with', excludes='age')class Person {    String firstName    String lastName    int age}def p = Person.maker().withFirstName("Robert").withLastName("Lewandowski").make()assert "$p.firstName $p.lastName" == "Robert Lewandowski"

This strategy also supports annotating static methods and constructors. In this case, the static method or constructorparameters become the properties to use for building purposes and in the case of static methods, the return typeof the method becomes the target class being built. If you have more than one@Builder annotation used withina class (at either the class, method or constructor positions) then it is up to you to ensure that the generatedhelper classes and factory methods have unique names (i.e. no more than one can use the default name values).Here is an example highlighting method and constructor usage (and also illustrating the renaming required for unique names).

import groovy.transform.builder.*import groovy.transform.*@ToString@Builderclass Person {  String first, last  int born  Person(){}  @Builder(builderClassName='MovieBuilder', builderMethodName='byRoleBuilder')  Person(String roleName) {     if (roleName == 'Jack Sparrow') {         this.first = 'Johnny'; this.last = 'Depp'; this.born = 1963     }  }  @Builder(builderClassName='NameBuilder', builderMethodName='nameBuilder', prefix='having', buildMethodName='fullName')  static String join(String first, String last) {      first + ' ' + last  }  @Builder(builderClassName='SplitBuilder', builderMethodName='splitBuilder')  static Person split(String name, int year) {      def parts = name.split(' ')      new Person(first: parts[0], last: parts[1], born: year)  }}assert Person.splitBuilder().name("Johnny Depp").year(1963).build().toString() == 'Person(Johnny, Depp, 1963)'assert Person.byRoleBuilder().roleName("Jack Sparrow").build().toString() == 'Person(Johnny, Depp, 1963)'assert Person.nameBuilder().havingFirst('Johnny').havingLast('Depp').fullName() == 'Johnny Depp'assert Person.builder().first("Johnny").last('Depp').born(1963).build().toString() == 'Person(Johnny, Depp, 1963)'

TheforClass annotation attribute is not supported for this strategy.

InitializerStrategy

To use theInitializerStrategy, annotate your Groovy class using the@Builder annotation, and specify the strategy as shown in this example:

import groovy.transform.builder.*import groovy.transform.*@ToString@Builder(builderStrategy=InitializerStrategy)class Person {    String firstName    String lastName    int age}

Your class will be locked down to have a single public constructor taking a "fully set" initializer.It will also have a factory method to create the initializer. These are used as follows:

@CompileStaticdef firstLastAge() {    assert new Person(Person.createInitializer().firstName("John").lastName("Smith").age(21)).toString() == 'Person(John, Smith, 21)'}firstLastAge()

Any attempt to use the initializer which doesn’t involve setting all the properties (though order is not important) will result ina compilation error. If you don’t need this level of strictness, you don’t need to use@CompileStatic.

You can use theInitializerStrategy in conjunction with@Canonical and@Immutable. If your@Builder annotationdoesn’t have explicitincludes orexcludes annotation attributes but your@Canonical annotation does, the onesfrom@Canonical will be re-used for@Builder. Here is an example using@Builder with@Immutable:

import groovy.transform.builder.*import groovy.transform.*import static groovy.transform.options.Visibility.PRIVATE@Builder(builderStrategy=InitializerStrategy)@Immutable@VisibilityOptions(PRIVATE)class Person {    String first    String last    int born}def publicCons = Person.constructorsassert publicCons.size() == 1@CompileStaticdef createFirstLastBorn() {  def p = new Person(Person.createInitializer().first('Johnny').last('Depp').born(1963))  assert "$p.first $p.last $p.born" == 'Johnny Depp 1963'}createFirstLastBorn()

The annotation attributeuseSetters can be used if you have a setter which you want called as part of theconstruction process. See the JavaDoc for details.

This strategy also supports annotating static methods and constructors. In this case, the static method or constructorparameters become the properties to use for building purposes and in the case of static methods, the return typeof the method becomes the target class being built. If you have more than one@Builder annotation used withina class (at either the class, method or constructor positions) then it is up to you to ensure that the generatedhelper classes and factory methods have unique names (i.e. no more than one can use the default name values).For an example of method and constructor usage but using theDefaultStrategy strategy, consult that strategy’sdocumentation.

The annotation attributeforClass is not supported for this strategy.

@groovy.transform.AutoImplement

The@AutoImplement AST transformation supplies dummy implementations for any found abstract methods fromsuperclasses or interfaces. The dummy implementation is the same for all abstract methods found and can be:

  • essentially empty (exactly true for void methods and for methods with a return type, returns the default value forthat type)

  • a statement that throws a specified exception (with optional message)

  • some user supplied code

The first example illustrates the default case. Our class is annotated with@AutoImplement,has a superclass and a single interface as can be seen here:

import groovy.transform.AutoImplement@AutoImplementclass MyNames extends AbstractList<String> implements Closeable { }

Avoid close() method from theCloseable interface is supplied and left empty. Implementations are also suppliedfor the three abstract methods from the super class. Theget,addAll andsize methodshave return types ofString,boolean andint respectively with default valuesnull,false and0. We can use our class (and check the expected return type for oneof the methods) using the following code:

assert new MyNames().size() == 0

It is also worthwhile examining the equivalent generated code:

class MyNames implements Closeable extends AbstractList<String> {    String get(int param0) {        return null    }    boolean addAll(Collection<? extends String> param0) {        return false    }    void close() throws Exception {    }    int size() {        return 0    }}

The second example illustrates the simplest exception case. Our class is annotated with@AutoImplement,has a superclass and an annotation attribute indicates that anIOException should be thrown if any ofour "dummy" methods are called. Here is the class definition:

@AutoImplement(exception=IOException)class MyWriter extends Writer { }

We can use the class (and check the expected exception is thrown for oneof the methods) using the following code:

import static groovy.test.GroovyAssert.shouldFailshouldFail(IOException) {  new MyWriter().flush()}

It is also worthwhile examining the equivalent generated code where three void methodshave been provided all of which throw the supplied exception:

class MyWriter extends Writer {    void flush() throws IOException {        throw new IOException()    }    void write(char[] param0, int param1, int param2) throws IOException {        throw new IOException()    }    void close() throws Exception {        throw new IOException()    }}

The third example illustrates the exception case with a supplied message. Our class is annotated with@AutoImplement,implements an interface, and has annotation attributes to indicate that anUnsupportedOperationException withNot supported by MyIterator as the message should be thrown for any supplied methods. Here is the class definition:

@AutoImplement(exception=UnsupportedOperationException, message='Not supported by MyIterator')class MyIterator implements Iterator<String> { }

We can use the class (and check the expected exception is thrown and has the correct messagefor one of the methods) using the following code:

def ex = shouldFail(UnsupportedOperationException) {     new MyIterator().hasNext()}assert ex.message == 'Not supported by MyIterator'

It is also worthwhile examining the equivalent generated code where three void methodshave been provided all of which throw the supplied exception:

class MyIterator implements Iterator<String> {    boolean hasNext() {        throw new UnsupportedOperationException('Not supported by MyIterator')    }    String next() {        throw new UnsupportedOperationException('Not supported by MyIterator')    }}

The fourth example illustrates the case of user supplied code. Our class is annotated with@AutoImplement,implements an interface, has an explicitly overriddenhasNext method, and has an annotation attribute containing thesupplied code for any supplied methods. Here is the class definition:

@AutoImplement(code = { throw new UnsupportedOperationException('Should never be called but was called on ' + new Date()) })class EmptyIterator implements Iterator<String> {    boolean hasNext() { false }}

We can use the class (and check the expected exception is thrown and has a message of the expected form)using the following code:

def ex = shouldFail(UnsupportedOperationException) {     new EmptyIterator().next()}assert ex.message.startsWith('Should never be called but was called on ')

It is also worthwhile examining the equivalent generated code where thenext method has been supplied:

class EmptyIterator implements java.util.Iterator<String> {    boolean hasNext() {        false    }    String next() {        throw new UnsupportedOperationException('Should never be called but was called on ' + new Date())    }}
@groovy.transform.NullCheck

The@NullCheck AST transformation adds null-check guard statements to constructors and methodswhich cause those methods to fail early when supplied with null arguments.It can be seen as a form of defensive programming.The annotation can be added to individual methods or constructors, or to the classin which case it will apply to all methods/constructors.

@NullCheckString longerOf(String first, String second) {    first.size() >= second.size() ? first : second}assert longerOf('cat', 'canary') == 'canary'def ex = shouldFail(IllegalArgumentException) {    longerOf('cat', null)}assert ex.message == 'second cannot be null'
Class design annotations

This category of annotations are aimed at simplifying the implementation of well-known design patterns (delegation,singleton, …​) by using a declarative style.

@groovy.transform.BaseScript

@BaseScript is used within scripts to indicate that the script shouldextend from a custom script base class rather thangroovy.lang.Script.See the documentation fordomain specific languages for further details.

@groovy.lang.Delegate

The@Delegate AST transformation aims at implementing the delegation design pattern. In the following class:

class Event {    @Delegate Date when    String title}

Thewhen property is annotated with@Delegate, meaning that theEvent class will delegate calls toDate methodsto thewhen property. In this case, the generated code looks like this:

class Event {    Date when    String title    boolean before(Date other) {        when.before(other)    }    // ...}

Then you can call thebefore method, for example, directly on theEvent class:

def ev = new Event(title:'Groovy keynote', when: Date.parse('yyyy/MM/dd', '2013/09/10'))def now = new Date()assert ev.before(now)

Instead of annotating a property (or field), you can also annotate a method.In this case, the method can be thought of as a getter or factory method for the delegate.As an example, here is a class which (rather unusually) has a pool of delegates which areaccessed in a round-robin fashion:

class Test {    private int robinCount = 0    private List<List> items = [[0], [1], [2]]    @Delegate    List getRoundRobinList() {        items[robinCount++ % items.size()]    }    void checkItems(List<List> testValue) {        assert items == testValue    }}

Here is an example usage of that class:

def t = new Test()t << 'fee't << 'fi't << 'fo't << 'fum't.checkItems([[0, 'fee', 'fum'], [1, 'fi'], [2, 'fo']])

Using a standard list in this round-robin fashion would violate many expected properties of lists, sodon’t expect the above class to do anything useful beyond this trivial example.

The behavior of the@Delegate AST transformation can be changed using the following parameters:

AttributeDefault valueDescriptionExample

interfaces

True

Should the interfaces implemented by the field be implemented by the class too

interface Greeter { void sayHello() }class MyGreeter implements Greeter { void sayHello() { println 'Hello!'} }class DelegatingGreeter { // no explicit interface    @Delegate MyGreeter greeter = new MyGreeter()}def greeter = new DelegatingGreeter()assert greeter instanceof Greeter // interface was added transparently

deprecated

false

If true, also delegates methods annotated with @Deprecated

class WithDeprecation {    @Deprecated    void foo() {}}class WithoutDeprecation {    @Deprecated    void bar() {}}class Delegating {    @Delegate(deprecated=true) WithDeprecation with = new WithDeprecation()    @Delegate WithoutDeprecation without = new WithoutDeprecation()}def d = new Delegating()d.foo() // passes thanks to deprecated=trued.bar() // fails because of @Deprecated

methodAnnotations

False

Whether to carry over annotations from the methods of the delegate to your delegating method.

class WithAnnotations {    @Transactional    void method() {    }}class DelegatingWithoutAnnotations {    @Delegate WithAnnotations delegate}class DelegatingWithAnnotations {    @Delegate(methodAnnotations = true) WithAnnotations delegate}def d1 = new DelegatingWithoutAnnotations()def d2 = new DelegatingWithAnnotations()assert d1.class.getDeclaredMethod('method').annotations.length==1assert d2.class.getDeclaredMethod('method').annotations.length==2

parameterAnnotations

False

Whether to carry over annotations from the method parameters of the delegate to your delegating method.

class WithAnnotations {    void method(@NotNull String str) {    }}class DelegatingWithoutAnnotations {    @Delegate WithAnnotations delegate}class DelegatingWithAnnotations {    @Delegate(parameterAnnotations = true) WithAnnotations delegate}def d1 = new DelegatingWithoutAnnotations()def d2 = new DelegatingWithAnnotations()assert d1.class.getDeclaredMethod('method',String).parameterAnnotations[0].length==0assert d2.class.getDeclaredMethod('method',String).parameterAnnotations[0].length==1

excludes

Empty array

A list of methods to be excluded from delegation. For more fine-grained control, see alsoexcludeTypes.

class Worker {    void task1() {}    void task2() {}}class Delegating {    @Delegate(excludes=['task2']) Worker worker = new Worker()}def d = new Delegating()d.task1() // passesd.task2() // fails because method is excluded

includes

Undefined marker array (indicates all methods)

A list of methods to be included in delegation. For morefine-grainedcontrol, see alsoincludeTypes.

class Worker {    void task1() {}    void task2() {}}class Delegating {    @Delegate(includes=['task1']) Worker worker = new Worker()}def d = new Delegating()d.task1() // passesd.task2() // fails because method is not included

excludeTypes

Empty array

A list of interfaces containing method signatures to be excluded from delegation

interface AppendStringSelector {    StringBuilder append(String str)}class UpperStringBuilder {    @Delegate(excludeTypes=AppendStringSelector)    StringBuilder sb1 = new StringBuilder()    @Delegate(includeTypes=AppendStringSelector)    StringBuilder sb2 = new StringBuilder()    String toString() { sb1.toString() + sb2.toString().toUpperCase() }}def usb = new UpperStringBuilder()usb.append(3.5d)usb.append('hello')usb.append(true)assert usb.toString() == '3.5trueHELLO'

includeTypes

Undefined marker array (indicates no list be default)

A list of interfaces containing method signatures to be included in delegation

interface AppendBooleanSelector {    StringBuilder append(boolean b)}interface AppendFloatSelector {    StringBuilder append(float b)}class NumberBooleanBuilder {    @Delegate(includeTypes=AppendBooleanSelector, interfaces=false)    StringBuilder nums = new StringBuilder()    @Delegate(includeTypes=[AppendFloatSelector], interfaces=false)    StringBuilder bools = new StringBuilder()    String result() { "${nums.toString()} ~ ${bools.toString()}" }}def b = new NumberBooleanBuilder()b.append(true)b.append(3.14f)b.append(false)b.append(0.0f)assert b.result() == "truefalse ~ 3.140.0"b.append(3.5d) // would fail because we didn't include append(double)

allNames

False

Should the delegate pattern be also applied to methods with internal names

class Worker {    void task$() {}}class Delegating {    @Delegate(allNames=true) Worker worker = new Worker()}def d = new Delegating()d.task$() //passes
@groovy.transform.Immutable

The@Immutable meta-annotation combines the following annotations:

The@Immutable meta-annotation simplifies the creation of immutable classes. Immutable classes are usefulsince they are often easier to reason about and are inherently thread-safe.SeeEffective Java, Minimize Mutability for all the detailsabout how to achieve immutable classes in Java. The@Immutable meta-annotation does most of the things describedinEffective Java for you automatically.To use the meta-annotation, all you have to do is annotate the class like in the following example:

import groovy.transform.Immutable@Immutableclass Point {    int x    int y}

One of the requirements for immutable classes is that there is no way to modify any state information within the class.One requirement to achieve this is to use immutable classes for each property or alternatively perform special codingsuch as defensive copy in and defensive copy out for any mutable properties within the constructorsand property getters. Between@ImmutableBase,@MapConstructor and@TupleConstructor propertiesare either identified as immutable or the special coding for numerous known cases is handled automatically.Various mechanisms are provided for you to extend the handled property types which are allowed. See@ImmutableOptions and@KnownImmutable for details.

The results of applying@Immutable to a class are pretty similar to those ofapplying the@Canonical meta-annotation but the generated class will have extralogic to handle immutability. You will observe this by, for instance, trying to modify a propertywhich will result in aReadOnlyPropertyException being thrown since the backing field for the propertywill have been automatically made final.

The@Immutable meta-annotation supports the configuration options found in the annotationsit aggregates. See those annotations for more details.

@groovy.transform.ImmutableBase

Immutable classes generated with@ImmutableBase are automatically made final. Also, the type of each property is checkedand various checks are made on the class, for example, public instance fields currently aren’t allowed. It also generatesacopyWith constructor if desired.

The following annotation attribute is supported:

AttributeDefault valueDescriptionExample

copyWith

false

A boolean whether to generate acopyWith( Map ) method.

import groovy.transform.Immutable@Immutable( copyWith=true )class User {    String  name    Integer age}def bob   = new User( 'bob', 43 )def alice = bob.copyWith( name:'alice' )assert alice.name == 'alice'assert alice.age  == 43
@groovy.transform.PropertyOptions

This annotation allows you to specify a custom property handler to be used by transformationsduring class construction. It is ignored by the main Groovy compiler but is referenced by other transformationslike@TupleConstructor,@MapConstructor, and@ImmutableBase. It is frequently used behind thescenes by the@Immutable meta-annotation.

@groovy.transform.VisibilityOptions

This annotation allows you to specify a custom visibility for a construct generated by another transformation.It is ignored by the main Groovy compiler but is referenced by other transformationslike@TupleConstructor,@MapConstructor, and@NamedVariant.

@groovy.transform.ImmutableOptions

Groovy’s immutability support relies on a predefined list of known immutable classes (likejava.net.URI orjava.lang.Stringand fails if you use a type which is not in that list, you are allowed to add to the list of known immutable typesthanks to the following annotation attributes of the@ImmutableOptions annotation:

AttributeDefault valueDescriptionExample

knownImmutableClasses

Empty list

A list of classes which are deemed immutable.

import groovy.transform.Immutableimport groovy.transform.TupleConstructor@TupleConstructorfinal class Point {    final int x    final int y    public String toString() { "($x,$y)" }}@Immutable(knownImmutableClasses=[Point])class Triangle {    Point a,b,c}

knownImmutables

Empty list

A list of property names which are deemed immutable.

import groovy.transform.Immutableimport groovy.transform.TupleConstructor@TupleConstructorfinal class Point {    final int x    final int y    public String toString() { "($x,$y)" }}@Immutable(knownImmutables=['a','b','c'])class Triangle {    Point a,b,c}

If you deem a type as immutable and it isn’t one of the ones automatically handled, then it is up to youto correctly code that class to ensure immutability.

@groovy.transform.KnownImmutable

The@KnownImmutable annotation isn’t actually one that triggers any AST transformations. It is simplya marker annotation. You can annotate your classes with the annotation (including Java classes) and theywill be recognized as acceptable types for members within an immutable class. This saves you having toexplicitly use theknownImmutables orknownImmutableClasses annotation attributes from@ImmutableOptions.

@groovy.transform.Memoized

The@Memoized AST transformations simplifies the implementation of caching by allowing the result of method callsto be cached just by annotating the method with@Memoized. Let’s imagine the following method:

long longComputation(int seed) {    // slow computation    Thread.sleep(100*seed)    System.nanoTime()}

This emulates a long computation, based on the actual parameters of the method. Without@Memoized, each method callwould take several seconds plus it would return a random result:

def x = longComputation(1)def y = longComputation(1)assert x!=y

Adding@Memoized changes the semantics of the method by adding caching, based on the parameters:

@Memoizedlong longComputation(int seed) {    // slow computation    Thread.sleep(100*seed)    System.nanoTime()}def x = longComputation(1) // returns after 100 millisecondsdef y = longComputation(1) // returns immediatelydef z = longComputation(2) // returns after 200 millisecondsassert x==yassert x!=z

The size of the cache can be configured using two optional parameters:

  • protectedCacheSize: the number of results which are guaranteed not to be cleared after garbage collection

  • maxCacheSize: the maximum number of results that can be kept in memory

By default, the size of the cache is unlimited and no cache result is protected from garbage collection. Setting aprotectedCacheSize>0 would create an unlimited cache with some results protected. SettingmaxCacheSize>0 would create a limited cache but without any protection from garbage protection. Setting both would create a limited, protected cache.

@groovy.transform.TailRecursive

The@TailRecursive annotation can be used to automatically transform a recursive call at the end of a methodinto an equivalent iterative version of the same code. This avoids stack overflow due to too many recursive calls.Below is an example of use when calculating factorial:

import groovy.transform.CompileStaticimport groovy.transform.TailRecursive@CompileStaticclass Factorial {    @TailRecursive    static BigInteger factorial( BigInteger i, BigInteger product = 1) {        if( i == 1) {            return product        }        return factorial(i-1, product*i)    }}assert Factorial.factorial(1) == 1assert Factorial.factorial(3) == 6assert Factorial.factorial(5) == 120assert Factorial.factorial(50000).toString().size() == 213237 // Big number and no Stack Overflow

Currently, the annotation will only work for self-recursive method calls, i.e. a single recursive call to the exact same method again.Consider using Closures andtrampoline() if you have a scenario involving simple mutual recursion.Also note that only non-void methods are currently handled (void calls will result in a compilation error).

Currently, some forms of method overloading can trick the compiler,and some non-tail recursive calls are erroneously treated as tail recursive.
@groovy.lang.Singleton

The@Singleton annotation can be used to implement the singleton design pattern on a class. The singleton instanceis defined eagerly by default, using class initialization, or lazily, in which case the field is initialized usingdouble-checked locking.

@Singletonclass GreetingService {    String greeting(String name) { "Hello, $name!" }}assert GreetingService.instance.greeting('Bob') == 'Hello, Bob!'

By default, the singleton is created eagerly when the class is initialized and available through theinstance property.It is possible to change the name of the singleton using theproperty parameter:

@Singleton(property='theOne')class GreetingService {    String greeting(String name) { "Hello, $name!" }}assert GreetingService.theOne.greeting('Bob') == 'Hello, Bob!'

And it is also possible to make initialization lazy using thelazy parameter:

class Collaborator {    public static boolean init = false}@Singleton(lazy=true,strict=false)class GreetingService {    static void init() {}    GreetingService() {        Collaborator.init = true    }    String greeting(String name) { "Hello, $name!" }}GreetingService.init() // make sure class is initializedassert Collaborator.init == falseGreetingService.instanceassert Collaborator.init == trueassert GreetingService.instance.greeting('Bob') == 'Hello, Bob!'

In this example, we also set thestrict parameter to false, which allows us to define our own constructor.

@groovy.lang.Mixin

Deprecated. Consider using traits instead.

Logging improvements

Groovy provides a family of AST transformations that help with integration of the most widelyused logging frameworks. There is a transform and associated annotation for each of the common frameworks.These transforms provide a streamlined declarative approach to using the logging framework.In each case, the transform will:

  • add a static finallog field to the annotated class corresponding to the logger

  • wrap all calls tolog.level() into the appropriatelog.isLevelEnabled guard, depending on the underlying framework

Those transformations support two parameters:

  • value (defaultlog) corresponds to the name of the logger field

  • category (defaults to the class name) is the name of the logger category

It’s worth noting that annotating a class with one of those annotations doesn’tprevent you from using the logging framework using the normal long-hand approach.

@groovy.util.logging.Log

The first logging AST transformation available is the@Log annotation which relies on the JDK logging framework. Writing:

@groovy.util.logging.Logclass Greeter {    void greet() {        log.info 'Called greeter'        println 'Hello, world!'    }}

is equivalent to writing:

import java.util.logging.Levelimport java.util.logging.Loggerclass Greeter {    private static final Logger log = Logger.getLogger(Greeter.name)    void greet() {        if (log.isLoggable(Level.INFO)) {            log.info 'Called greeter'        }        println 'Hello, world!'    }}
@groovy.util.logging.Commons

Groovy supports theApache Commons Logging framework using the@Commons annotation. Writing:

@groovy.util.logging.Commonsclass Greeter {    void greet() {        log.debug 'Called greeter'        println 'Hello, world!'    }}

is equivalent to writing:

import org.apache.commons.logging.LogFactoryimport org.apache.commons.logging.Logclass Greeter {    private static final Log log = LogFactory.getLog(Greeter)    void greet() {        if (log.isDebugEnabled()) {            log.debug 'Called greeter'        }        println 'Hello, world!'    }}

You still need to add the appropriate commons-logging jar to your classpath.

@groovy.util.logging.Log4j

Groovy supports theApache Log4j 1.x framework using the@Log4j annotation. Writing:

@groovy.util.logging.Log4jclass Greeter {    void greet() {        log.debug 'Called greeter'        println 'Hello, world!'    }}

is equivalent to writing:

import org.apache.log4j.Loggerclass Greeter {    private static final Logger log = Logger.getLogger(Greeter)    void greet() {        if (log.isDebugEnabled()) {            log.debug 'Called greeter'        }        println 'Hello, world!'    }}

You still need to add the appropriate log4j jar to your classpath.This annotation can also be used with the compatiblereload4j log4jdrop-in replacement, just use the jar from that project instead of a log4j jar.

@groovy.util.logging.Log4j2

Groovy supports theApache Log4j 2.x framework using the@Log4j2 annotation. Writing:

@groovy.util.logging.Log4j2class Greeter {    void greet() {        log.debug 'Called greeter'        println 'Hello, world!'    }}

is equivalent to writing:

import org.apache.logging.log4j.LogManagerimport org.apache.logging.log4j.Loggerclass Greeter {    private static final Logger log = LogManager.getLogger(Greeter)    void greet() {        if (log.isDebugEnabled()) {            log.debug 'Called greeter'        }        println 'Hello, world!'    }}

You still need to add the appropriate log4j2 jar to your classpath.

@groovy.util.logging.Slf4j

Groovy supports theSimple Logging Facade for Java (SLF4J) framework using the@Slf4j annotation. Writing:

@groovy.util.logging.Slf4jclass Greeter {    void greet() {        log.debug 'Called greeter'        println 'Hello, world!'    }}

is equivalent to writing:

import org.slf4j.LoggerFactoryimport org.slf4j.Loggerclass Greeter {    private static final Logger log = LoggerFactory.getLogger(Greeter)    void greet() {        if (log.isDebugEnabled()) {            log.debug 'Called greeter'        }        println 'Hello, world!'    }}

You still need to add the appropriate slf4j jar(s) to your classpath.

@groovy.util.logging.PlatformLog

Groovy supports theJava Platform Logging API and Serviceframework using the@PlatformLog annotation. Writing:

@groovy.util.logging.PlatformLogclass Greeter {    void greet() {        log.info 'Called greeter'        println 'Hello, world!'    }}

is equivalent to writing:

import java.lang.System.Loggerimport java.lang.System.LoggerFinderimport static java.lang.System.Logger.Level.INFOclass Greeter {    private static final transient Logger log =        LoggerFinder.loggerFinder.getLogger(Greeter.class.name, Greeter.class.module)    void greet() {        log.log INFO, 'Called greeter'        println 'Hello, world!'    }}

You need to be using JDK 9+ to use this capability.

Declarative concurrency

The Groovy language provides a set of annotations aimed at simplifying common concurrency patterns in a declarativeapproach.

@groovy.transform.Synchronized

The@Synchronized AST transformations works in a similar way to thesynchronized keyword but locks on differentobjects for safer concurrency. It can be applied on any method or static method:

import groovy.transform.Synchronizedimport java.util.concurrent.Executorsimport java.util.concurrent.TimeUnitclass Counter {    int cpt    @Synchronized    int incrementAndGet() {        cpt++    }    int get() {        cpt    }}

Writing this is equivalent to creating a lock object and wrapping the whole method into a synchronized block:

class Counter {    int cpt    private final Object $lock = new Object()    int incrementAndGet() {        synchronized($lock) {            cpt++        }    }    int get() {        cpt    }}

By default,@Synchronized creates a field named$lock (or$LOCK for a static method) but you can make it use anyfield you want by specifying the value attribute, like in the following example:

import groovy.transform.Synchronizedimport java.util.concurrent.Executorsimport java.util.concurrent.TimeUnitclass Counter {    int cpt    private final Object myLock = new Object()    @Synchronized('myLock')    int incrementAndGet() {        cpt++    }    int get() {        cpt    }}
@groovy.transform.WithReadLock and@groovy.transform.WithWriteLock

The@WithReadLock AST transformation works in conjunction with the@WithWriteLock transformationto provide read/write synchronization using theReentrantReadWriteLock facility that the JDK provides. The annotationcan be added to a method or a static method. It will transparently create a$reentrantLock final field (or$REENTRANTLOCK for a static method) and proper synchronization code will be added. For example, the following code:

import groovy.transform.WithReadLockimport groovy.transform.WithWriteLockclass Counters {    public final Map<String,Integer> map = [:].withDefault { 0 }    @WithReadLock    int get(String id) {        map.get(id)    }    @WithWriteLock    void add(String id, int num) {        Thread.sleep(200) // emulate long computation        map.put(id, map.get(id)+num)    }}

is equivalent to this:

import groovy.transform.WithReadLock as WithReadLockimport groovy.transform.WithWriteLock as WithWriteLockpublic class Counters {    private final Map<String, Integer> map    private final java.util.concurrent.locks.ReentrantReadWriteLock $reentrantlock    public int get(java.lang.String id) {        $reentrantlock.readLock().lock()        try {            map.get(id)        }        finally {            $reentrantlock.readLock().unlock()        }    }    public void add(java.lang.String id, int num) {        $reentrantlock.writeLock().lock()        try {            java.lang.Thread.sleep(200)            map.put(id, map.get(id) + num )        }        finally {            $reentrantlock.writeLock().unlock()        }    }}

Both@WithReadLock and@WithWriteLock support specifying an alternative lock object. In that case, the referenced field must be declared by the user, like in the following alternative:

import groovy.transform.WithReadLockimport groovy.transform.WithWriteLockimport java.util.concurrent.locks.ReentrantReadWriteLockclass Counters {    public final Map<String,Integer> map = [:].withDefault { 0 }    private final ReentrantReadWriteLock customLock = new ReentrantReadWriteLock()    @WithReadLock('customLock')    int get(String id) {        map.get(id)    }    @WithWriteLock('customLock')    void add(String id, int num) {        Thread.sleep(200) // emulate long computation        map.put(id, map.get(id)+num)    }}

For details

Easier cloning and externalizing

Groovy provides two annotations aimed at facilitating the implementation ofCloneable andExternalizable interfaces,respectively named@AutoClone and@AutoExternalize.

@groovy.transform.AutoClone

The@AutoClone annotation is aimed at implementing the@java.lang.Cloneable interface using various strategies, thanks to thestyle parameter:

  • the defaultAutoCloneStyle.CLONE strategy callssuper.clone() first thenclone() on each cloneable property

  • theAutoCloneStyle.SIMPLE strategy uses a regular constructor call and copies properties from the source to the clone

  • theAutoCloneStyle.COPY_CONSTRUCTOR strategy creates and uses a copy constructor

  • theAutoCloneStyle.SERIALIZATION strategy uses serialization (or externalization) to clone the object

Each of those strategies have pros and cons which are discussed in the Javadoc forgroovy.transform.AutoClone andgroovy.transform.AutoCloneStyle .

For example, the following example:

import groovy.transform.AutoClone@AutoCloneclass Book {    String isbn    String title    List<String> authors    Date publicationDate}

is equivalent to this:

class Book implements Cloneable {    String isbn    String title    List<String> authors    Date publicationDate    public Book clone() throws CloneNotSupportedException {        Book result = super.clone()        result.authors = authors instanceof Cloneable ? (List) authors.clone() : authors        result.publicationDate = publicationDate.clone()        result    }}

Note that the String properties aren’t explicitly handled because Strings are immutable and theclone() method fromObject will copy the String references. The same would apply to primitive fields and most of the concrete subclasses ofjava.lang.Number.

In addition to cloning styles,@AutoClone supports multiple options:

AttributeDefault valueDescriptionExample

excludes

Empty list

A list of property or field names that need to be excluded from cloning. A string consisting of a comma-separated field/property names is also allowed.Seegroovy.transform.AutoClone#excludes for details

import groovy.transform.AutoCloneimport groovy.transform.AutoCloneStyle@AutoClone(style=AutoCloneStyle.SIMPLE,excludes='authors')class Book {    String isbn    String title    List authors    Date publicationDate}

includeFields

false

By default, only properties are cloned. Setting this flag to true will also clone fields.

import groovy.transform.AutoCloneimport groovy.transform.AutoCloneStyle@AutoClone(style=AutoCloneStyle.SIMPLE,includeFields=true)class Book {    String isbn    String title    List authors    protected Date publicationDate}
@groovy.transform.AutoExternalize

The@AutoExternalize AST transformation will assist in the creation ofjava.io.Externalizable classes. It willautomatically add the interface to the class and generate thewriteExternal andreadExternal methods. For example, thiscode:

import groovy.transform.AutoExternalize@AutoExternalizeclass Book {    String isbn    String title    float price}

will be converted into:

class Book implements java.io.Externalizable {    String isbn    String title    float price    void writeExternal(ObjectOutput out) throws IOException {        out.writeObject(isbn)        out.writeObject(title)        out.writeFloat( price )    }    public void readExternal(ObjectInput oin) {        isbn = (String) oin.readObject()        title = (String) oin.readObject()        price = oin.readFloat()    }}

The@AutoExternalize annotation supports two parameters which will let you slightly customize its behavior:

AttributeDefault valueDescriptionExample

excludes

Empty list

A list of property or field names that need to be excluded from externalizing. A string consisting of a comma-separated field/property names is also allowed.Seegroovy.transform.AutoExternalize#excludes for details

import groovy.transform.AutoExternalize@AutoExternalize(excludes='price')class Book {    String isbn    String title    float price}

includeFields

false

By default, only properties are externalized. Setting this flag to true will also clone fields.

import groovy.transform.AutoExternalize@AutoExternalize(includeFields=true)class Book {    String isbn    String title    protected float price}
Safer scripting

The Groovy language makes it easy to execute user scripts at runtime (for example usinggroovy.lang.GroovyShell),but how do you make sure that a script won’t eat all CPU (infinite loops) or that concurrent scripts won’t slowly consumeall available threads of a thread pool? Groovy provides several annotations which are aimed towards safer scripting,generating code which will for example allow you to interrupt execution automatically.

@groovy.transform.ThreadInterrupt

One complicated situation in the JVM world is when a thread can’t be stopped. TheThread#stop method exists but isdeprecated (and isn’t reliable) so your only chance lies inThread#interrupt. Calling the latter will set theinterrupt flag on the thread, but it willnot stop the execution of the thread. This is problematic because it’s theresponsibility of the code executing in the thread to check the interrupt flag and properly exit. This makes sense whenyou, as a developer, know that the code you are executing is meant to be run in an independent thread, but in general,you don’t know it. It’s even worse with user scripts, who might not even know what a thread is (think of DSLs).

@ThreadInterrupt simplifies this by adding thread interruption checks at critical places in the code:

  • loops (for, while)

  • first instruction of a method

  • first instruction of a closure body

Let’s imagine the following user script:

while (true) {    i++}

This is an obvious infinite loop. If this code executes in its own thread, interrupting wouldn’t help: if youjoin onthe thread, then the calling code would be able to continue, but the thread would still be alive, running in backgroundwithout any ability for you to stop it, slowly causing thread starvation.

One possibility to work around this is to set up your shell this way:

def config = new CompilerConfiguration()config.addCompilationCustomizers(        new ASTTransformationCustomizer(ThreadInterrupt))def binding = new Binding(i:0)def shell = new GroovyShell(binding,config)

The shell is then configured to automatically apply the@ThreadInterrupt AST transformations on all scripts. This allowsyou to execute user scripts this way:

def t = Thread.start {    shell.evaluate(userCode)}t.join(1000) // give at most 1000ms for the script to completeif (t.alive) {    t.interrupt()}

The transformation automatically modified user code like this:

while (true) {    if (Thread.currentThread().interrupted) {        throw new InterruptedException('The current thread has been interrupted.')    }    i++}

The check which is introduced inside the loop guarantees that if theinterrupt flag is set on the current thread, anexception will be thrown, interrupting the execution of the thread.

@ThreadInterrupt supports multiple options that will let you further customize the behavior of the transformation:

AttributeDefault valueDescriptionExample

thrown

java.lang.InterruptedException

Specifies the type of exception which is thrown if the thread is interrupted.

class BadException extends Exception {    BadException(String message) { super(message) }}def config = new CompilerConfiguration()config.addCompilationCustomizers(        new ASTTransformationCustomizer(thrown:BadException, ThreadInterrupt))def binding = new Binding(i:0)def shell = new GroovyShell(this.class.classLoader,binding,config)def userCode = """try {    while (true) {        i++    }} catch (BadException e) {    i = -1}"""def t = Thread.start {    shell.evaluate(userCode)}t.join(1000) // give at most 1s for the script to completeassert binding.i > 0if (t.alive) {    t.interrupt()}Thread.sleep(500)assert binding.i == -1'''

checkOnMethodStart

true

Should an interruption check be inserted at the beginning of each method body. Seegroovy.transform.ThreadInterrupt for details.

@ThreadInterrupt(checkOnMethodStart=false)

applyToAllClasses

true

Should the transformation be applied on all classes of the same source unit (in the same source file). Seegroovy.transform.ThreadInterrupt for details.

@ThreadInterrupt(applyToAllClasses=false)class A { ... } // interrupt checks addedclass B { ... } // no interrupt checks

applyToAllMembers

true

Should the transformation be applied on all members of class. Seegroovy.transform.ThreadInterrupt for details.

class A {    @ThreadInterrupt(applyToAllMembers=false)    void method1() { ... } // interrupt checked added    void method2() { ... } // no interrupt checks}
@groovy.transform.TimedInterrupt

The@TimedInterrupt AST transformation tries to solve a slightly different problem from@groovy.transform.ThreadInterrupt: instead of checking theinterrupt flag of the thread, it will automaticallythrow an exception if the thread has been running for too long.

This annotation doesnot spawn a monitoring thread. Instead, it works in a similar manner as@ThreadInterrupt by placing checks at appropriate places in the code. This means that if youhave a thread blocked by I/O, it willnot be interrupted.

Imagine the following user code:

def fib(int n) { n<2?n:fib(n-1)+fib(n-2) }result = fib(600)

The implementation of the famous Fibonacci number computation here is far from optimized. If it is called with a highn value, it can take minutes to answer. With@TimedInterrupt, you canchoose how long a script is allowed to run. The following setup code will allow the user script to run for 1 second at max:

def config = new CompilerConfiguration()config.addCompilationCustomizers(        new ASTTransformationCustomizer(value:1, TimedInterrupt))def binding = new Binding(result:0)def shell = new GroovyShell(this.class.classLoader, binding,config)

This code is equivalent to annotating a class with@TimedInterrupt like this:

@TimedInterrupt(value=1, unit=TimeUnit.SECONDS)class MyClass {    def fib(int n) {        n<2?n:fib(n-1)+fib(n-2)    }}

@TimedInterrupt supports multiple options that will let you further customize the behavior of the transformation:

AttributeDefault valueDescriptionExample

value

Long.MAX_VALUE

Used in combination withunit to specify after how long execution times out.

@TimedInterrupt(value=500L, unit= TimeUnit.MILLISECONDS, applyToAllClasses = false)class Slow {    def fib(n) { n<2?n:fib(n-1)+fib(n-2) }}def resultdef t = Thread.start {    result = new Slow().fib(500)}t.join(5000)assert result == nullassert !t.alive

unit

TimeUnit.SECONDS

Used in combination withvalue to specify after how long execution times out.

@TimedInterrupt(value=500L, unit= TimeUnit.MILLISECONDS, applyToAllClasses = false)class Slow {    def fib(n) { n<2?n:fib(n-1)+fib(n-2) }}def resultdef t = Thread.start {    result = new Slow().fib(500)}t.join(5000)assert result == nullassert !t.alive

thrown

java.util.concurrent.TimeoutException

Specifies the type of exception which is thrown if timeout is reached.

@TimedInterrupt(thrown=TooLongException, applyToAllClasses = false, value=1L)class Slow {    def fib(n) { Thread.sleep(100); n<2?n:fib(n-1)+fib(n-2) }}def resultdef t = Thread.start {    try {        result = new Slow().fib(50)    } catch (TooLongException e) {        result = -1    }}t.join(5000)assert result == -1

checkOnMethodStart

true

Should an interruption check be inserted at the beginning of each method body. Seegroovy.transform.TimedInterrupt for details.

@TimedInterrupt(checkOnMethodStart=false)

applyToAllClasses

true

Should the transformation be applied on all classes of the same source unit (in the same source file). Seegroovy.transform.TimedInterrupt for details.

@TimedInterrupt(applyToAllClasses=false)class A { ... } // interrupt checks addedclass B { ... } // no interrupt checks

applyToAllMembers

true

Should the transformation be applied on all members of class. Seegroovy.transform.TimedInterrupt for details.

class A {    @TimedInterrupt(applyToAllMembers=false)    void method1() { ... } // interrupt checked added    void method2() { ... } // no interrupt checks}
@TimedInterrupt is currently not compatible with static methods!
@groovy.transform.ConditionalInterrupt

The last annotation for safer scripting is the base annotation when you want to interrupt a script using a custom strategy. In particular, this is the annotation of choice if youwant to use resource management (limit the number of calls to an API, …​). In the following example, user code is using an infinite loop, but@ConditionalInterrupt will allow usto check a quota manager and interrupt automatically the script:

@ConditionalInterrupt({Quotas.disallow('user')})class UserCode {    void doSomething() {        int i=0        while (true) {            println "Consuming resources ${++i}"        }    }}

The quota checking is very basic here, but it can be any code:

class Quotas {    static def quotas = [:].withDefault { 10 }    static boolean disallow(String userName) {        println "Checking quota for $userName"        (quotas[userName]--)<0    }}

We can make sure@ConditionalInterrupt works properly using this test code:

assert Quotas.quotas['user'] == 10def t = Thread.start {    new UserCode().doSomething()}t.join(5000)assert !t.aliveassert Quotas.quotas['user'] < 0

Of course, in practice, it is unlikely that@ConditionalInterrupt will be itself added by hand on user code. It can be injected in a similar manner as the example shown in theThreadInterrupt section, using theorg.codehaus.groovy.control.customizers.ASTTransformationCustomizer :

def config = new CompilerConfiguration()def checkExpression = new ClosureExpression(        Parameter.EMPTY_ARRAY,        new ExpressionStatement(                new MethodCallExpression(new ClassExpression(ClassHelper.make(Quotas)), 'disallow', new ConstantExpression('user'))        ))config.addCompilationCustomizers(        new ASTTransformationCustomizer(value: checkExpression, ConditionalInterrupt))def shell = new GroovyShell(this.class.classLoader,new Binding(),config)def userCode = """        int i=0        while (true) {            println "Consuming resources \\${++i}"        }"""assert Quotas.quotas['user'] == 10def t = Thread.start {    shell.evaluate(userCode)}t.join(5000)assert !t.aliveassert Quotas.quotas['user'] < 0

@ConditionalInterrupt supports multiple options that will let you further customize the behavior of the transformation:

AttributeDefault valueDescriptionExample

value

The closure which will be called to check if execution is allowed. If the closure returns false, execution is allowed. If it returns true, then an exception will be thrown.

@ConditionalInterrupt({ ... })

thrown

java.lang.InterruptedException

Specifies the type of exception which is thrown if execution should be aborted.

config.addCompilationCustomizers(        new ASTTransformationCustomizer(thrown: QuotaExceededException,value: checkExpression, ConditionalInterrupt))assert Quotas.quotas['user'] == 10def t = Thread.start {    try {        shell.evaluate(userCode)    } catch (QuotaExceededException) {        Quotas.quotas['user'] = 'Quota exceeded'    }}t.join(5000)assert !t.aliveassert Quotas.quotas['user'] == 'Quota exceeded'

checkOnMethodStart

true

Should an interruption check be inserted at the beginning of each method body. Seegroovy.transform.ConditionalInterrupt for details.

@ConditionalInterrupt(checkOnMethodStart=false)

applyToAllClasses

true

Should the transformation be applied on all classes of the same source unit (in the same source file). Seegroovy.transform.ConditionalInterrupt for details.

@ConditionalInterrupt(applyToAllClasses=false)class A { ... } // interrupt checks addedclass B { ... } // no interrupt checks

applyToAllMembers

true

Should the transformation be applied on all members of class. Seegroovy.transform.ConditionalInterrupt for details.

class A {    @ConditionalInterrupt(applyToAllMembers=false)    void method1() { ... } // interrupt checked added    void method2() { ... } // no interrupt checks}
Compiler directives

This category of AST transformations groups annotations which have a direct impact on the semantics of the code, ratherthan focusing on code generation. With that regards, they can be seen as compiler directives that either change thebehavior of a program at compile time or runtime.

@groovy.transform.Field

The@Field annotation only makes sense in the context of a script and aims at solving a common scoping error withscripts. The following example will for example fail at runtime:

def xString line() {    "="*x}x=3assert "===" == line()x=5assert "=====" == line()

The error that is thrown may be difficult to interpret: groovy.lang.MissingPropertyException: No such property: x. The reason is that scripts are compiledto classes and the script body is itself compiled as a singlerun() method. Methods which are defined in the scripts are independent, so the code above isequivalent to this:

class MyScript extends Script {    String line() {        "="*x    }    public def run() {        def x        x=3        assert "===" == line()        x=5        assert "=====" == line()    }}

Sodef x is effectively interpreted as a local variable, outside of the scope of theline method. The@Field AST transformation aims at fixing thisby changing the scope of the variable to a field of the enclosing script:

@Field def xString line() {    "="*x}x=3assert "===" == line()x=5assert "=====" == line()

The resulting, equivalent, code is now:

class MyScript extends Script {    def x    String line() {        "="*x    }    public def run() {        x=3        assert "===" == line()        x=5        assert "=====" == line()    }}
@groovy.transform.PackageScope

By default, Groovy visibility rules imply that if you create a field without specifying a modifier, then the field is interpreted as a property:

class Person {    String name // this is a property}

Should you want to create a package private field instead of a property (private field+getter/setter), then annotate your field with@PackageScope:

class Person {    @PackageScope String name // not a property anymore}

The@PackageScope annotation can also be used for classes, methods and constructors. In addition, by specifying a listofPackageScopeTarget values as the annotation attribute at the class level, all members within that class that don’thave an explicit modifier and match the providedPackageScopeTarget will remain package protected. For example to applyto fields within a class use the following annotation:

import static groovy.transform.PackageScopeTarget.FIELDS@PackageScope(FIELDS)class Person {  String name     // not a property, package protected  Date dob        // not a property, package protected  private int age // explicit modifier, so won't be touched}

The@PackageScope annotation is seldom used as part of normal Groovy conventions but is sometimes usefulfor factory methods that should be visible internally within a package or for methods or constructors providedfor testing purposes, or when integrating with third-party libraries which require such visibility conventions.

@groovy.transform.Final

@Final is essentially an alias for thefinal modifier.The intention is that you would almost never use the@Final annotation directly (just usefinal).However, when creating meta-annotations that should applythe final modifier to the node being annotated, you can mix in@Final, e.g..

@AnnotationCollector([Singleton,Final]) @interface MySingleton {}@MySingletonclass GreetingService {    String greeting(String name) { "Hello, $name!" }}assert GreetingService.instance.greeting('Bob') == 'Hello, Bob!'assert Modifier.isFinal(GreetingService.modifiers)
@groovy.transform.AutoFinal

The@AutoFinal annotation instructs the compiler to automatically insert the final modifierin numerous places within the annotated node. If applied on a method (or constructor), the parametersfor that method (or constructor) will be marked as final. If applied on a class definition, the sametreatment will occur for all declared methods and constructors within that class.

It is often considered bad practice to reassign parameters of a method or constructor with its body.By adding the final modifier to all parameter declarations you can avoid this practice entirely.Some programmers feel that adding final everywhere increases the amount of boilerplate code and makes themethod signatures somewhat noisy. An alternative might instead be to use a code review process or applyacodenarcruleto give warnings if that practice is observed but these alternatives might lead to delayed feedback duringquality checking rather than within the IDE or during compilation. The@AutoFinal annotation aims tomaximise compiler/IDE feedback while retaining succinct code with minimum boilerplate noise.

The following example illustrates applying the annotation at the class level:

import groovy.transform.AutoFinal@AutoFinalclass Person {    private String first, last    Person(String first, String last) {        this.first = first        this.last = last    }    String fullName(String separator) {        "$first$separator$last"    }    String greeting(String salutation) {        "$salutation, $first"    }}

In this example, the two parameters for the constructor and the single parameter forboth thefullname andgreeting methods will be final. Attempts to modify those parameters within theconstructor or method bodies will be flagged by the compiler.

The following example illustrates applying the annotation at the method level:

class Calc {    @AutoFinal    int add(int a, int b) { a + b }    int mult(int a, int b) { a * b }}

Here, theadd method will have final parameters but themult method will remain unchanged.

@groovy.transform.AnnotationCollector

@AnnotationCollector allows the creation of meta-annotations, which are described in adedicated section.

@groovy.transform.TypeChecked

@TypeChecked activates compile-time type checking on your Groovy code. Seesection on type checking for details.

@groovy.transform.CompileStatic

@CompileStatic activates static compilation on your Groovy code. Seesection on type checking for details.

@groovy.transform.CompileDynamic

@CompileDynamic disables static compilation on parts of your Groovy code. Seesection on type checking for details.

@groovy.lang.DelegatesTo

@DelegatesTo is not, technically speaking, an AST transformation. It is aimed at documenting code and helping the compiler in case you areusingtype checking orstatic compilation. The annotation is described thoroughly in theDSL section of this guide.

@groovy.transform.SelfType

@SelfType is not an AST transformation but rather a marker interface usedwith traits. See thetraits documentation for further details.

Swing patterns
@groovy.beans.Bindable

@Bindable is an AST transformation that transforms a regular property into a bound property (according to theJavaBeans specification).The@Bindable annotation can be placed on a property or a class. To convert all properties of a class into bound properties, on can annotate the class like in this example:

import groovy.beans.Bindable@Bindableclass Person {    String name    int age}

This is equivalent to writing this:

import java.beans.PropertyChangeListenerimport java.beans.PropertyChangeSupportclass Person {    final private PropertyChangeSupport this$propertyChangeSupport    String name    int age    public void addPropertyChangeListener(PropertyChangeListener listener) {        this$propertyChangeSupport.addPropertyChangeListener(listener)    }    public void addPropertyChangeListener(String name, PropertyChangeListener listener) {        this$propertyChangeSupport.addPropertyChangeListener(name, listener)    }    public void removePropertyChangeListener(PropertyChangeListener listener) {        this$propertyChangeSupport.removePropertyChangeListener(listener)    }    public void removePropertyChangeListener(String name, PropertyChangeListener listener) {        this$propertyChangeSupport.removePropertyChangeListener(name, listener)    }    public void firePropertyChange(String name, Object oldValue, Object newValue) {        this$propertyChangeSupport.firePropertyChange(name, oldValue, newValue)    }    public PropertyChangeListener[] getPropertyChangeListeners() {        return this$propertyChangeSupport.getPropertyChangeListeners()    }    public PropertyChangeListener[] getPropertyChangeListeners(String name) {        return this$propertyChangeSupport.getPropertyChangeListeners(name)    }}

@Bindable therefore removes a lot of boilerplate from your class, dramatically increasing readability. If the annotation is put on a single property, only that property is bound:

import groovy.beans.Bindableclass Person {    String name    @Bindable int age}
@groovy.beans.ListenerList

The@ListenerList AST transformation generates code for adding, removing and getting the list of listeners to a class, just by annotating a collection property:

import java.awt.event.ActionListenerimport groovy.beans.ListenerListclass Component {    @ListenerList    List<ActionListener> listeners;}

The transform will generate the appropriate add/remove methods based on the generic type of the list. In addition, it will also createfireXXX methods based on the public methods declared on the class:

import java.awt.event.ActionEventimport java.awt.event.ActionListener as ActionListenerimport groovy.beans.ListenerList as ListenerListpublic class Component {    @ListenerList    private List<ActionListener> listeners    public void addActionListener(ActionListener listener) {        if ( listener == null) {            return        }        if ( listeners == null) {            listeners = []        }        listeners.add(listener)    }    public void removeActionListener(ActionListener listener) {        if ( listener == null) {            return        }        if ( listeners == null) {            listeners = []        }        listeners.remove(listener)    }    public ActionListener[] getActionListeners() {        Object __result = []        if ( listeners != null) {            __result.addAll(listeners)        }        return (( __result ) as ActionListener[])    }    public void fireActionPerformed(ActionEvent param0) {        if ( listeners != null) {            ArrayList<ActionListener> __list = new ArrayList<ActionListener>(listeners)            for (def listener : __list ) {                listener.actionPerformed(param0)            }        }    }}

@Bindable supports multiple options that will let you further customize the behavior of the transformation:

AttributeDefault valueDescriptionExample

name

Generic type name

By default, the suffix which will be appended to add/remove/…​ methods is the simple class name of the generic type of the list.

class Component {    @ListenerList(name='item')    List<ActionListener> listeners;}

synchronize

false

If set to true, generated methods will be synchronized

class Component {    @ListenerList(synchronize = true)    List<ActionListener> listeners;}
@groovy.beans.Vetoable

The@Vetoable annotation works in a similar manner to@Bindable but generates constrained property according to the JavaBeans specification, instead of bound properties. The annotationcan be placed on a class, meaning that all properties will be converted to constrained properties, or on a single property. For example, annotating this class with@Vetoable:

import groovy.beans.Vetoableimport java.beans.PropertyVetoExceptionimport java.beans.VetoableChangeListener@Vetoableclass Person {    String name    int age}

is equivalent to writing this:

public class Person {    private String name    private int age    final private java.beans.VetoableChangeSupport this$vetoableChangeSupport    public void addVetoableChangeListener(VetoableChangeListener listener) {        this$vetoableChangeSupport.addVetoableChangeListener(listener)    }    public void addVetoableChangeListener(String name, VetoableChangeListener listener) {        this$vetoableChangeSupport.addVetoableChangeListener(name, listener)    }    public void removeVetoableChangeListener(VetoableChangeListener listener) {        this$vetoableChangeSupport.removeVetoableChangeListener(listener)    }    public void removeVetoableChangeListener(String name, VetoableChangeListener listener) {        this$vetoableChangeSupport.removeVetoableChangeListener(name, listener)    }    public void fireVetoableChange(String name, Object oldValue, Object newValue) throws PropertyVetoException {        this$vetoableChangeSupport.fireVetoableChange(name, oldValue, newValue)    }    public VetoableChangeListener[] getVetoableChangeListeners() {        return this$vetoableChangeSupport.getVetoableChangeListeners()    }    public VetoableChangeListener[] getVetoableChangeListeners(String name) {        return this$vetoableChangeSupport.getVetoableChangeListeners(name)    }    public void setName(String value) throws PropertyVetoException {        this.fireVetoableChange('name', name, value)        name = value    }    public void setAge(int value) throws PropertyVetoException {        this.fireVetoableChange('age', age, value)        age = value    }}

If the annotation is put on a single property, only that property is made vetoable:

import groovy.beans.Vetoableclass Person {    String name    @Vetoable int age}
Test assistance
@groovy.test.NotYetImplemented

@NotYetImplemented is used to invert the result of a JUnit 3/4 test case. It is in particular useful if a feature is not yet implemented but the test is. In that case, it is expectedthat the test fails. Marking it with@NotYetImplemented will inverse the result of the test, like in this example:

import groovy.test.GroovyTestCaseimport groovy.test.NotYetImplementedclass Maths {    static int fib(int n) {        // todo: implement later    }}class MathsTest extends GroovyTestCase {    @NotYetImplemented    void testFib() {        def dataTable = [                1:1,                2:1,                3:2,                4:3,                5:5,                6:8,                7:13        ]        dataTable.each { i, r ->            assert Maths.fib(i) == r        }    }}

Another advantage of using this technique is that you can write test cases for bugs before knowing how to fix them. If some time in the future, a modification in the code fixes a bug by side effect,you’ll be notified because a test which was expected to fail passed.

@groovy.transform.ASTTest

@ASTTest is a special AST transformation meant to help debugging other AST transformations or the Groovy compiler itself. It will let the developer "explore" the AST during compilation andperform assertions on the AST rather than on the result of compilation. This means that this AST transformations gives access to the AST before the bytecode is produced.@ASTTest can beplaced on any annotable node and requires two parameters:

  • phase: sets at which phase at which@ASTTest will be triggered. The test code will work on the AST tree at the end of this phase.

  • value: the code which will be executed once the phase is reached, on the annotated node

Compile phase has to be chosen from one oforg.codehaus.groovy.control.CompilePhase . However, since it is not possible to annotate a node twice with the same annotation, you willnot be able to use@ASTTest on the same node at two distinct compile phases.

value is a closure expression which has access to a special variablenode corresponding to the annotated node, and a helperlookup method which will be discussedhere.For example, you can annotate a class node like this:

import groovy.transform.ASTTestimport org.codehaus.groovy.ast.ClassNode@ASTTest(phase=CONVERSION, value={(1)    assert node instanceof ClassNode(2)    assert node.name == 'Person'(3)})class Person {}
1we’re checking the state of the Abstract Syntax Tree after the CONVERSION phase
2node refers to the AST node which is annotated by @ASTTest
3it can be used to perform assertions at compile time

One interesting feature of@ASTTest is that if an assertion fails, thencompilation will fail. Now imagine that we want to check the behavior of an AST transformation at compile time.We will take@PackageScope here, and we will want to verify that a property annotated with@PackageScope becomes a package private field. For this, we have to know at which phase thetransform runs, which can be found inorg.codehaus.groovy.transform.PackageScopeASTTransformation : semantic analysis. Then a test can be written like this:

import groovy.transform.ASTTestimport groovy.transform.PackageScope@ASTTest(phase=SEMANTIC_ANALYSIS, value={    def nameNode = node.properties.find { it.name == 'name' }    def ageNode = node.properties.find { it.name == 'age' }    assert nameNode    assert ageNode == null // shouldn't be a property anymore    def ageField = node.getDeclaredField 'age'    assert ageField.modifiers == 0})class Person {    String name    @PackageScope int age}

The@ASTTest annotation can only be placed wherever the grammar allows it. Sometimes, you would like to test the contents of an AST node which is not annotable. In this case,@ASTTest provides a convenientlookup method which will search the AST for nodes which are labelled with a special token:

def list = lookup('anchor')(1)Statement stmt = list[0](2)
1returns the list of AST nodes which label is 'anchor'
2it is always necessary to choose which element to process since lookup always returns a list

Imagine, for example, that you want to test the declared type of a for loop variable. Then you can do it like this:

import groovy.transform.ASTTestimport groovy.transform.PackageScopeimport org.codehaus.groovy.ast.ClassHelperimport org.codehaus.groovy.ast.expr.DeclarationExpressionimport org.codehaus.groovy.ast.stmt.ForStatementclass Something {    @ASTTest(phase=SEMANTIC_ANALYSIS, value={        def forLoop = lookup('anchor')[0]        assert forLoop instanceof ForStatement        def decl = forLoop.collectionExpression.expressions[0]        assert decl instanceof DeclarationExpression        assert decl.variableExpression.name == 'i'        assert decl.variableExpression.originType == ClassHelper.int_TYPE    })    void someMethod() {        int x = 1;        int y = 10;        anchor: for (int i=0; i<x+y; i++) {            println "$i"        }    }}

@ASTTest also exposes those variables inside the test closure:

  • node corresponds to the annotated node, as usual

  • compilationUnit gives access to the currentorg.codehaus.groovy.control.CompilationUnit

  • compilePhase returns the current compile phase (org.codehaus.groovy.control.CompilePhase)

The latter is interesting if you don’t specify thephase attribute. In that case, the closure will be executed aftereach compile phase after (and including)SEMANTIC_ANALYSIS. The context of the transformation is kept after each phase,giving you a chance to check what changed between two phases.

As an example, here is how you could dump the list of AST transformations registered on a class node:

import groovy.transform.ASTTestimport groovy.transform.CompileStaticimport groovy.transform.Immutableimport org.codehaus.groovy.ast.ClassNodeimport org.codehaus.groovy.control.CompilePhase@ASTTest(value={    System.err.println "Compile phase: $compilePhase"    ClassNode cn = node    System.err.println "Global AST xforms: ${compilationUnit?.ASTTransformationsContext?.globalTransformNames}"    CompilePhase.values().each {        def transforms = cn.getTransforms(it)        if (transforms) {            System.err.println "Ast xforms for phase $it:"            transforms.each { map ->                System.err.println(map)            }        }    }})@CompileStatic@Immutableclass Foo {}

And here is how you can memorize variables for testing between two phases:

import groovy.transform.ASTTestimport groovy.transform.ToStringimport org.codehaus.groovy.ast.ClassNodeimport org.codehaus.groovy.control.CompilePhase@ASTTest(value={    if (compilePhase == CompilePhase.INSTRUCTION_SELECTION) {(1)        println "toString() was added at phase: ${added}"        assert added == CompilePhase.CANONICALIZATION(2)    } else {        if (node.getDeclaredMethods('toString') && added == null) {(3)            added = compilePhase(4)        }    }})@ToStringclass Foo {    String name}
1if the current compile phase is instruction selection
2then we want to make suretoString was added atCANONICALIZATION
3otherwise, iftoString exists and that the variable from the context,added is null
4then it means that this compile phase is the one wheretoString was added
Grape handling
@groovy.lang.Grapes

Grape is a dependency management engine embedded into Groovy, relying on several annotations which are describedthoroughly in thissection of the guide.

Developing AST transformations

There are two kinds of transformations: global and local transformations.

  • Global transformations are applied to by the compiler on the code being compiled,wherever the transformation apply. Compiled classes that implement global transformationsare in a JAR added to the classpath of the compiler and contain service locator fileMETA-INF/services/org.codehaus.groovy.transform.ASTTransformation with a line with the name of thetransformation class. The transformation class must have a no-args constructor and implement theorg.codehaus.groovy.transform.ASTTransformation interface.It will be run againstevery source in the compilation, so be sure to not create transformations whichscan all the AST in an expansive and time-consuming manner, to keep the compiler fast.

  • Local transformations are transformations applied locally by annotating code elements you want totransform. For this, we reuse the annotation notation, and those annotations should implementorg.codehaus.groovy.transform.ASTTransformation. The compiler will discover them and apply thetransformation on these code elements.

Compilation phases guide

Groovy AST transformations must be performed in one of the nine definedcompilation phases (org.codehaus.groovy.control.CompilePhase).

Global transformations may be applied in any phase, but localtransformations may only be applied in the semantic analysis phase orlater. Briefly, the compiler phases are:

  • Initialization: source files are opened and environment configured

  • Parsing: the grammar is used to produce tree of tokens representingthe source code

  • Conversion: An abstract syntax tree (AST) is created from token trees.

  • Semantic Analysis: Performs consistency and validity checks that thegrammar can’t check for, and resolves classes.

  • Canonicalization: Complete building the AST

  • Instruction Selection: instruction set is chosen, for example Java 6 or Java 7 bytecode level

  • Class Generation: creates the bytecode of the class in memory

  • Output: write the binary output to the file system

  • Finalization: Perform any last cleanup

Generally speaking, there is more type information available later inthe phases. If your transformation is concerned with reading the AST,then a later phase where information is more plentiful might be a goodchoice. If your transformation is concerned with writing AST, then anearlier phase where the tree is more sparse might be more convenient.

Local transformations

Local AST transformations are relative to the context they are applied to. Inmost cases, the context is defined by an annotation that will define the scopeof the transform. For example, annotating a field would mean that the transformationapplies to the field, while annotating the class would mean that the transformationapplies to the whole class.

As a naive and simple example, consider wanting to write a@WithLoggingtransformation that would add console messages at the start and end of amethod invocation. So the following "Hello World" example wouldactually print "Hello World" along with a start and stop message:

Poor man’s aspect oriented programming
@WithLoggingdef greet() {    println "Hello World"}greet()

A local AST transformation is an easy way to do this. It requires two things:

AnASTTransformation is a callback that gives you access to theorg.codehaus.groovy.control.SourceUnit,through which you can get a reference to theorg.codehaus.groovy.ast.ModuleNode (AST).

The AST (Abstract Syntax Tree) is a tree structure consisting mostly oforg.codehaus.groovy.ast.expr.Expression (expressions) ororg.codehaus.groovy.ast.expr.Statement (statements). An easy way tolearn about the AST is to explore it in a debugger. Once you have the AST,you can analyze it to find out information about the code or rewrite it to addnew functionality.

The local transformation annotation is the simple part. Here is the@WithLogging one:

import org.codehaus.groovy.transform.GroovyASTTransformationClassimport java.lang.annotation.ElementTypeimport java.lang.annotation.Retentionimport java.lang.annotation.RetentionPolicyimport java.lang.annotation.Target@Retention(RetentionPolicy.SOURCE)@Target([ElementType.METHOD])@GroovyASTTransformationClass(["gep.WithLoggingASTTransformation"])public @interface WithLogging {}

The annotation retention can beSOURCE because you won’t need the annotationpast that. The element type here isMETHOD, the@WithLogging because the annotationapplies to methods.

But the most important part is the@GroovyASTTransformationClass annotation. This links the@WithLoggingannotation to theASTTransformation class you will write.gep.WithLoggingASTTransformation is the fully qualified class name of theASTTransformation we are going to write. This line wires the annotation to the transformation.

With this in place, the Groovy compiler is going to invokegep.WithLoggingASTTransformation every time an@WithLogging is found in asource unit. Any breakpoint set withinLoggingASTTransformation will nowbe hit within the IDE when running the sample script.

TheASTTransformation class is a little more complex. Here is thevery simple, and very naive, transformation to add a method start andstop message for@WithLogging:

@CompileStatic(1)@GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)(2)class WithLoggingASTTransformation implements ASTTransformation {(3)    @Override    void visit(ASTNode[] nodes, SourceUnit sourceUnit) {(4)        MethodNode method = (MethodNode) nodes[1](5)        def startMessage = createPrintlnAst("Starting $method.name")(6)        def endMessage = createPrintlnAst("Ending $method.name")(7)        def existingStatements = ((BlockStatement)method.code).statements(8)        existingStatements.add(0, startMessage)(9)        existingStatements.add(endMessage)(10)    }    private static Statement createPrintlnAst(String message) {(11)        new ExpressionStatement(            new MethodCallExpression(                new VariableExpression("this"),                new ConstantExpression("println"),                new ArgumentListExpression(                    new ConstantExpression(message)                )            )        )    }}
1even if not mandatory, if you write an AST transformation in Groovy, it is highly recommended to useCompileStaticbecause it will improve performance of the compiler.
2annotate withorg.codehaus.groovy.transform.GroovyASTTransformation to tell at which compilation phase thetransform needs to run. Here, it’s at thesemantic analysis phase.
3implement theASTTransformation interface
4which only has a singlevisit method
5thenodes parameter is a 2 AST node array, for which the first one is the annotation node (@WithLogging) andthe second one is the annotated node (the method node)
6create a statement that will print a message when we enter the method
7create a statement that will print a message when we exit the method
8get the method body, which in this case is aBlockStatement
9add the enter method message before the first statement of existing code
10append the exit method message after the last statement of existing code
11creates anExpressionStatement wrapping aMethodCallExpression corresponding tothis.println("message")

It is important to notice that for the brevity of this example, we didn’t make the necessary checks, such as checkingthat the annotated node is really aMethodNode, or that the method body is an instance ofBlockStatement. Thisexercise is left to the reader.

Note the creation of the new println statements in thecreatePrintlnAst(String) method. Creating AST for code is not alwayssimple. In this case we need to construct a new method call, passing inthe receiver/variable, the name of the method, and an argument list.When creating AST, it might be helpful to write the code you’re tryingto create in a Groovy file and then inspect the AST of that code in thedebugger to learn what to create. Then write a function likecreatePrintlnAst using what you learned through the debugger.

In the end:

@WithLoggingdef greet() {    println "Hello World"}greet()

Produces:

Starting greetHello WorldEnding greet
It is important to note that an AST transformation participates directly in the compilation process. A commonerror by beginners is to have the AST transformation code in the same source tree as a class that uses the transformation.Being in the same source tree in general means that they are compiled at the same time. Since the transformation itselfis going to be compiled in phases and that each compile phase processes all files of the same source unit before goingto the next one, there’s a direct consequence: the transformation will not be compiled before the class that uses it! Inconclusion, AST transformations need to be precompiled before you can use them. In general, it is as easy as having themin a separate source tree.
Global transformations

Global AST transformation are similar to local one with a major difference: they do not need an annotation, meaning thatthey are appliedglobally, that is to say on each class being compiled. It is therefore very important to limit theiruse to last resort, because it can have a significant impact on the compiler performance.

Following the example of thelocal AST transformation, imagine that we would like to trace allmethods, and not only those which are annotated with@WithLogging. Basically, we need this code to behave the sameas the one annotated with@WithLogging before:

def greet() {    println "Hello World"}greet()

To make this work, there are two steps:

  1. create theorg.codehaus.groovy.transform.ASTTransformation descriptor inside theMETA-INF/services directory

  2. create theASTTransformation implementation

The descriptor file is required and must be found on classpath. It will contain a single line:

META-INF/services/org.codehaus.groovy.transform.ASTTransformation
gep.WithLoggingASTTransformation

The code for the transformation looks similar to the local case, but instead of using theASTNode[] parameter, we needto use theSourceUnit instead:

gep/WithLoggingASTTransformation.groovy
@CompileStatic(1)@GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)(2)class WithLoggingASTTransformation implements ASTTransformation {(3)    @Override    void visit(ASTNode[] nodes, SourceUnit sourceUnit) {(4)        def methods = sourceUnit.AST.methods(5)        methods.each { method ->(6)            def startMessage = createPrintlnAst("Starting $method.name")(7)            def endMessage = createPrintlnAst("Ending $method.name")(8)            def existingStatements = ((BlockStatement)method.code).statements(9)            existingStatements.add(0, startMessage)(10)            existingStatements.add(endMessage)(11)        }    }    private static Statement createPrintlnAst(String message) {(12)        new ExpressionStatement(            new MethodCallExpression(                new VariableExpression("this"),                new ConstantExpression("println"),                new ArgumentListExpression(                    new ConstantExpression(message)                )            )        )    }}
1even if not mandatory, if you write an AST transformation in Groovy, it is highly recommended to useCompileStaticbecause it will improve performance of the compiler.
2annotate withorg.codehaus.groovy.transform.GroovyASTTransformation to tell at which compilation phase thetransform needs to run. Here, it’s at thesemantic analysis phase.
3implement theASTTransformation interface
4which only has a singlevisit method
5thesourceUnit parameter gives access to the source being compiled, so we get the AST of the current sourceand retrieve the list of methods from this file
6we iterate on each method from the source file
7create a statement that will print a message when we enter the method
8create a statement that will print a message when we exit the method
9get the method body, which in this case is aBlockStatement
10add the enter method message before the first statement of existing code
11append the exit method message after the last statement of existing code
12creates anExpressionStatement wrapping aMethodCallExpression corresponding tothis.println("message")
AST API guide
AbstractASTTransformation

While you have seen that you can directly implement theASTTransformation interface, in almost all cases you will notdo this but extend theorg.codehaus.groovy.transform.AbstractASTTransformation class. This class provides severalutility methods that make AST transformations easier to write. Almost all AST transformations included in Groovyextend this class.

ClassCodeExpressionTransformer

It is a common use case to be able to transform an expression into another. Groovy provides a class which makes itvery easy to do this:org.codehaus.groovy.ast.ClassCodeExpressionTransformer

To illustrate this, let’s create a@Shout transformation that will transform allString constants in method callarguments into their uppercase version. For example:

@Shoutdef greet() {    println "Hello World"}greet()

should print:

HELLO WORLD

Then the code for the transformation can use theClassCodeExpressionTransformer to make this easier:

@CompileStatic@GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)class ShoutASTTransformation implements ASTTransformation {    @Override    void visit(ASTNode[] nodes, SourceUnit sourceUnit) {        ClassCodeExpressionTransformer trn = new ClassCodeExpressionTransformer() {(1)            private boolean inArgList = false            @Override            protected SourceUnit getSourceUnit() {                sourceUnit(2)            }            @Override            Expression transform(final Expression exp) {                if (exp instanceof ArgumentListExpression) {                    inArgList = true                } else if (inArgList &&                    exp instanceof ConstantExpression && exp.value instanceof String) {                    return new ConstantExpression(exp.value.toUpperCase())(3)                }                def trn = super.transform(exp)                inArgList = false                trn            }        }        trn.visitMethod((MethodNode)nodes[1])(4)    }}
1Internally the transformation creates aClassCodeExpressionTransformer
2The transformer needs to return the source unit
3if a constant expression of type string is detected inside an argument list, transform it into its upper case version
4call the transformer on the method being annotated
AST Nodes
Writing an AST transformation requires a deep knowledge of the internal Groovy API. In particular it requiresknowledge about the AST classes. Since those classes are internal, there are chances that the API will change in thefuture, meaning that your transformationscould break. Despite that warning, the AST has been very stable over timeand such a thing rarely happens.

Classes of the Abstract Syntax Tree belong to theorg.codehaus.groovy.ast package. It is recommended to the readerto use the Groovy Console, in particular the AST browser tool, to gain knowledge about those classes.Another resource for learning is theAST Buildertest suite.

Macros
Introduction

Until version 2.5.0, when developing AST transformations, developers should have a deep knowledge about how the AST(Abstract Syntax Tree) was built by the compiler in order to know how to add new expressions or statements duringcompile time.

Although the use oforg.codehaus.groovy.ast.tool.GeneralUtils static methods could mitigate the burden of creatingexpressions and statements, it’s still a low-level way of writing those AST nodes directly.We needed something to abstract us from writing the AST directly and that’s exactly what Groovy macros were made for.They allow you to directly add code during compilation, without having to translate the code you had in mind to theorg.codehaus.groovy.ast.* node related classes.

Statements and expressions

Let’s see an example, lets create a local AST transformation:@AddMessageMethod. When applied to a given class itwill add a new method calledgetMessage to that class. The method will return "42". The annotation is prettystraight forward:

@Retention(RetentionPolicy.SOURCE)@Target([ElementType.TYPE])@GroovyASTTransformationClass(["metaprogramming.AddMethodASTTransformation"])@interface AddMethod { }

What would the AST transformation look like without the use of a macro ? Something like this:

@GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)class AddMethodASTTransformation extends AbstractASTTransformation {    @Override    void visit(ASTNode[] nodes, SourceUnit source) {        ClassNode classNode = (ClassNode) nodes[1]        ReturnStatement code =                new ReturnStatement((1)                        new ConstantExpression("42"))(2)        MethodNode methodNode =                new MethodNode(                        "getMessage",                        ACC_PUBLIC,                        ClassHelper.make(String),                        [] as Parameter[],                        [] as ClassNode[],                        code)(3)        classNode.addMethod(methodNode)(4)    }}
1Create a return statement
2Create a constant expression "42"
3Adding the code to the new method
4Adding the new method to the annotated class

If you’re not used to the AST API, that definitely doesn’t look like the code you had in mind. Now look how theprevious code simplifies with the use of macros.

@GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)class AddMethodWithMacrosASTTransformation extends AbstractASTTransformation {    @Override    void visit(ASTNode[] nodes, SourceUnit source) {        ClassNode classNode = (ClassNode) nodes[1]        ReturnStatement simplestCode = macro { return "42" }(1)        MethodNode methodNode =                new MethodNode(                        "getMessage",                        ACC_PUBLIC,                        ClassHelper.make(String),                        [] as Parameter[],                        [] as ClassNode[],                        simplestCode)(2)        classNode.addMethod(methodNode)(3)    }}
1Much simpler. You wanted to add a return statement that returned "42" and that’s exactly what you can read insidethemacro utility method. Your plain code will be translated for you to aorg.codehaus.groovy.ast.stmt.ReturnStatement
2Adding the return statement to the new method
3Adding the new code to the annotated class

Although themacro method is used in this example to create astatement themacro method could also be used to createexpressions as well, it depends on whichmacro signature you use:

  • macro(Closure): Create a given statement with the code inside the closure.

  • macro(Boolean,Closure): iftrue wrap expressions inside the closure inside a statement, iffalse then returnan expression

  • macro(CompilePhase, Closure): Create a given statement with the code inside the closure in a specific compile phase

  • macro(CompilePhase, Boolean, Closure): Create a statement or an expression (true == statement, false == expression)in a specific compilation phase.

All these signatures can be found atorg.codehaus.groovy.macro.runtime.MacroGroovyMethods

Sometimes we could be only interested in creating a given expression, not the whole statement, in order to do that weshould use any of themacro invocations with a boolean parameter:

@GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)class AddGetTwoASTTransformation extends AbstractASTTransformation {    BinaryExpression onePlusOne() {        return macro(false) { 1 + 1 }(1)    }    @Override    void visit(ASTNode[] nodes, SourceUnit source) {        ClassNode classNode = nodes[1]        BinaryExpression expression = onePlusOne()(2)        ReturnStatement returnStatement = GeneralUtils.returnS(expression)(3)        MethodNode methodNode =                new MethodNode("getTwo",                        ACC_PUBLIC,                        ClassHelper.Integer_TYPE,                        [] as Parameter[],                        [] as ClassNode[],                        returnStatement(4)                )        classNode.addMethod(methodNode)(5)    }}
1We’re telling macro not to wrap the expression in a statement, we’re only interested in the expression
2Assigning the expression
3Creating aReturnStatement using a method fromGeneralUtils and returning the expression
4Adding the code to the new method
5Adding the method to the class
Variable substitution

Macros are great but we can’t create anything useful or reusable if our macros couldn’t receive parameters or resolvesurrounding variables.

In the following example we’re creating an AST transformation@MD5 that when applied to a given String field willadd a method returning the MD5 value of that field.

@Retention(RetentionPolicy.SOURCE)@Target([ElementType.FIELD])@GroovyASTTransformationClass(["metaprogramming.MD5ASTTransformation"])@interface MD5 { }

And the transformation:

@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)class MD5ASTTransformation extends AbstractASTTransformation {    @Override    void visit(ASTNode[] nodes, SourceUnit source) {        FieldNode fieldNode = nodes[1]        ClassNode classNode = fieldNode.declaringClass        String capitalizedName = fieldNode.name.capitalize()        MethodNode methodNode = new MethodNode(                "get${capitalizedName}MD5",                ACC_PUBLIC,                ClassHelper.STRING_TYPE,                [] as Parameter[],                [] as ClassNode[],                buildMD5MethodCode(fieldNode))        classNode.addMethod(methodNode)    }    BlockStatement buildMD5MethodCode(FieldNode fieldNode) {        VariableExpression fieldVar = GeneralUtils.varX(fieldNode.name)(1)        return macro(CompilePhase.SEMANTIC_ANALYSIS, true) {(2)            return java.security.MessageDigest                    .getInstance('MD5')                    .digest($v { fieldVar }.getBytes())(3)                    .encodeHex()                    .toString()        }    }}
1We need a reference to a variable expression
2If using a class outside the standard packages we should add any needed imports or use the qualified name. Whenusing the qualified name of a given static method you need to make sure it’s resolved in the proper compile phase. Inthis particular case we’re instructing the macro to resolve it at the SEMANTIC_ANALYSIS phase, which is the first compile phasewith type information.
3In order to substitute anyexpression inside the macro we need to use the$v method.$v receives a closure as anargument, and the closure is only allowed to substitute expressions, meaning classes inheritingorg.codehaus.groovy.ast.expr.Expression.
MacroClass

As we mentioned earlier, themacro method is only capable of producingstatements andexpressions. But what if wewant to produce other types of nodes, such as a method, a field and so on?

org.codehaus.groovy.macro.transform.MacroClass can be used to createclasses (ClassNode instances) in ourtransformations the same way we created statements and expressions with themacro method before.

The next example is a local transformation@Statistics. When applied to a given class, it will add two methodsgetMethodCount() andgetFieldCount() which return how many methods and fields within the class respectively. Hereis the marker annotation.

@Retention(RetentionPolicy.SOURCE)@Target([ElementType.TYPE])@GroovyASTTransformationClass(["metaprogramming.StatisticsASTTransformation"])@interface Statistics {}

And the AST transformation:

@CompileStatic@GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)class StatisticsASTTransformation extends AbstractASTTransformation {    @Override    void visit(ASTNode[] nodes, SourceUnit source) {        ClassNode classNode = (ClassNode) nodes[1]        ClassNode templateClass = buildTemplateClass(classNode)(1)        templateClass.methods.each { MethodNode node ->(2)            classNode.addMethod(node)        }    }    @CompileDynamic    ClassNode buildTemplateClass(ClassNode reference) {(3)        def methodCount = constX(reference.methods.size())(4)        def fieldCount = constX(reference.fields.size())(5)        return new MacroClass() {            class Statistics {                java.lang.Integer getMethodCount() {(6)                    return $v { methodCount }                }                java.lang.Integer getFieldCount() {(7)                    return $v { fieldCount }                }            }        }    }}
1Creating a template class
2Adding template class methods to the annotated class
3Passing the reference class
4Extracting reference class method count value expression
5Extracting reference class field count value expression
6Building thegetMethodCount() method using reference’s method count value expression
7Building thegetFieldCount() method using reference’s field count value expression

Basically we’ve created theStatistics class as a template to avoid writing low level AST API, then wecopied methods created in the template class to their final destination.

Types inside theMacroClass implementation should be resolved inside, that’s why we had to writejava.lang.Integer instead of simply writingInteger.
Notice that we’re using@CompileDynamic. That’s because the way we useMacroClass is like wewere actually implementing it. So if you were using@CompileStatic it will complain because an implementation ofan abstract class can’t be another different class.
@Macro methods

You have seen that by usingmacro you can save yourself a lot of work but you might wonder wherethat method came from. You didn’t declare it or static import it. You can think of it as a specialglobal method (or if you prefer, a method on everyObject). This is much like how theprintlnextension method is defined. But unlikeprintln which becomes a method selected for executionlater in the compilation process,macro expansion is done early in the compilation process.The declaration ofmacro as one of the available methods for this early expansion is doneby annotating amacro method definition with the@Macro annotation and making that methodavailable using a similar mechanism for extension modules. Such methods are known asmacro methodsand the good news is you can define your own.

To define your own macro method, create a class in a similar way to an extension module andadd a method such as:

public class ExampleMacroMethods {    @Macro    public static Expression safe(MacroContext macroContext, MethodCallExpression callExpression) {        return ternaryX(                notNullX(callExpression.getObjectExpression()),                callExpression,                constX(null)        );    }    ...}

Now you would register this as an extension module using aorg.codehaus.groovy.runtime.ExtensionModulefile within theMETA-INF/groovy directory.

Now, assuming that the class and meta info file are on your classpath, you can use themacro method in the following way:

def nullObject = nullassert null == safe(safe(nullObject.hashcode()).toString())
Testing AST transformations
Separating source trees

This section is about good practices in regard to testing AST transformations. Previous sections highlighted the factthat to be able to execute an AST transformation, it has to be precompiled. It might sound obvious but a lot of peopleget caught on this, trying to use an AST transformation in the same source tree as where it is defined.

The first tip for testing AST transformation is therefore to separate test sources from the sources of the transform.Again, this is nothing but best practices, but you must make sure that your build too does actually compile them separately.This is the case by default with bothApache Maven andGradle.

Debugging AST transformations

It is very handy to be able to put a breakpoint in an AST transformation, so that you can debug your code in the IDE.However, you might be surprised to see that your IDE doesn’t stop on the breakpoint. The reason is actually simple: ifyour IDE uses the Groovy compiler to compile the unit tests for your AST transformation, then the compilation is triggeredfrom the IDE, but the process which will compile the files doesn’t have debugging options. It’s only when the test caseis executed that the debugging options are set on the virtual machine. In short: it is too late, the class has been compiledalready, and your transformation is already applied.

A very easy workaround is to use theGroovyTestCase class which provides anassertScript method. This means thatinstead of writing this in a test case:

static class Subject {    @MyTransformToDebug    void methodToBeTested() {}}void testMyTransform() {    def c = new Subject()    c.methodToBeTested()}

You should write:

void testMyTransformWithBreakpoint() {    assertScript '''        import metaprogramming.MyTransformToDebug        class Subject {            @MyTransformToDebug            void methodToBeTested() {}        }        def c = new Subject()        c.methodToBeTested()    '''}

The difference is that when you useassertScript, the code in theassertScript block is compiledwhen theunit test is executed. That is to say that this time, theSubject class will be compiled with debugging active, andthe breakpoint is going to be hit.

ASTMatcher

Sometimes you may want to make assertions over AST nodes; perhaps to filter the nodes, or to make sure a giventransformation has built the expected AST node.

Filtering nodes

For instance if you would like to apply a given transformation only to a specific set of AST nodes, you coulduseASTMatcher to filter these nodes. The following example shows how to transform a given expression toanother. UsingASTMatcher it looks for a specific expression1 + 1 and it transforms it to3. That’s whywe called it the@Joking example.

First we create the@Joking annotation that only can be applied to methods:

@Retention(RetentionPolicy.SOURCE)@Target([ElementType.METHOD])@GroovyASTTransformationClass(["metaprogramming.JokingASTTransformation"])@interface Joking { }

Then the transformation, that only applies an instance oforg.codehaus.groovy.ast.ClassCodeExpressionTransformerto all the expressions within the method code block.

@CompileStatic@GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)class JokingASTTransformation extends AbstractASTTransformation {    @Override    void visit(ASTNode[] nodes, SourceUnit source) {        MethodNode methodNode = (MethodNode) nodes[1]        methodNode            .getCode()            .visit(new ConvertOnePlusOneToThree(source))(1)    }}
1Get the method’s code statement and apply the expression transformer

And this is when theASTMatcher is used to apply the transformation only to those expressions matchingthe expression1 + 1.

class ConvertOnePlusOneToThree extends ClassCodeExpressionTransformer {    SourceUnit sourceUnit    ConvertOnePlusOneToThree(SourceUnit sourceUnit) {        this.sourceUnit = sourceUnit    }    @Override    Expression transform(Expression exp) {        Expression ref = macro { 1 + 1 }(1)        if (ASTMatcher.matches(ref, exp)) {(2)            return macro { 3 }(3)        }        return super.transform(exp)    }}
1Builds the expression used as reference pattern
2Checks the current expression evaluated matches the reference expression
3If it matches then replaces the current expression with the expression built withmacro

Then you could test the implementation as follows:

package metaprogrammingclass Something {    @Joking    Integer getResult() {        return 1 + 1    }}assert new Something().result == 3

Unit testing AST transforms

Normally we test AST transformations just checking that the final use of the transformation does what we expect. Butit would be great if we could have an easy way to check, for example, that the nodes the transformation adds are whatwe expected from the beginning.

The following transformation adds a new methodgiveMeTwo to an annotated class.

@GroovyASTTransformation(phase = CompilePhase.INSTRUCTION_SELECTION)class TwiceASTTransformation extends AbstractASTTransformation {    static final String VAR_X = 'x'    @Override    void visit(ASTNode[] nodes, SourceUnit source) {        ClassNode classNode = (ClassNode) nodes[1]        MethodNode giveMeTwo = getTemplateClass(sumExpression)            .getDeclaredMethods('giveMeTwo')            .first()        classNode.addMethod(giveMeTwo)(1)    }    BinaryExpression getSumExpression() {(2)        return macro {            $v{ varX(VAR_X) } +            $v{ varX(VAR_X) }        }    }    ClassNode getTemplateClass(Expression expression) {(3)        return new MacroClass() {            class Template {                java.lang.Integer giveMeTwo(java.lang.Integer x) {                    return $v { expression }                }            }        }    }}
1Adding the method to the annotated class
2Building a binary expression. The binary expression uses the same variable expression in bothsides of the+ token (checkvarX method atorg.codehaus.groovy.ast.tool.GeneralUtils).
3Builds a newClassNode with a method calledgiveMeTwo which returns the result of an expressionpassed as parameter.

Now instead of creating a test executing the transformation over a given sample code. I would like to check thatthe construction of the binary expression is done properly:

void testTestingSumExpression() {    use(ASTMatcher) {(1)        TwiceASTTransformation sample = new TwiceASTTransformation()        Expression referenceNode = macro {            a + a(2)        }.withConstraints {(3)            placeholder 'a'(4)        }        assert sample            .sumExpression            .matches(referenceNode)(5)    }}
1Using ASTMatcher as a category
2Build a template node
3Apply some constraints to that template node
4Tells compiler thata is a placeholder.
5Asserts reference node and current node are equal

Of course you can/should always check the actual execution:

void testASTBehavior() {    assertScript '''    package metaprogramming    @Twice    class AAA {    }    assert new AAA().giveMeTwo(1) == 2    '''}
ASTTest

Last but not least, testing an AST transformation is also about testing the state of the ASTduring compilation. Groovyprovides a tool named@ASTTest for this: it is an annotation that will let you add assertions on an abstract syntaxtree. Please check thedocumentation for ASTTest for more details.

External references

If you are interested in a step-by-step tutorial about writing AST transformations, you can followthis workshop.

3.5. Dependency management with Grape

3.5.1. Quick start

Add a Dependency

Grape is a JAR dependency manager embedded into Groovy. Grape lets you quickly add maven repository dependencies to yourclasspath, making scripting even easier. The simplest use is as simple as adding an annotation to your script:

@Grab(group='org.springframework', module='spring-orm', version='5.2.8.RELEASE')import org.springframework.jdbc.core.JdbcTemplate

@Grab also supports a shorthand notation:

@Grab('org.springframework:spring-orm:5.2.8.RELEASE')import org.springframework.jdbc.core.JdbcTemplate

Note that we are using an annotated import here, which is the recommended way. You can also search fordependencies onmvnrepository.com and it willprovide you the@Grab annotation form of thepom.xml entry.

Specify Additional Repositories

Not all dependencies are in maven central. You can add new ones likethis:

@GrabResolver(name='restlet', root='http://maven.restlet.org/')@Grab(group='org.restlet', module='org.restlet', version='1.1.6')
Maven Classifiers

Some maven dependencies need classifiers in order to be able to resolve.You can fix that like this:

@Grab(group='net.sf.json-lib', module='json-lib', version='2.2.3', classifier='jdk15')
Excluding Transitive Dependencies

Sometimes you will want to exclude transitive dependencies as you mightbe already using a slightly different but compatible version of someartifact. You can do this as follows:

@Grab('net.sourceforge.htmlunit:htmlunit:2.8')@GrabExclude('xml-apis:xml-apis')
JDBC Drivers

Because of the way JDBC drivers are loaded, you’ll need to configureGrape to attach JDBC driver dependencies to the system class loader.I.e:

@GrabConfig(systemClassLoader=true)@Grab(group='mysql', module='mysql-connector-java', version='5.1.6')
Using Grape From the Groovy Shell

From groovysh use the method call variant:

groovy.grape.Grape.grab(group:'org.springframework', module:'spring', version:'2.5.6')
Proxy settings

If you are behind a firewall and/or need to use Groovy/Grape through aproxy server, you can specify those settings on the command like via thehttp.proxyHost andhttp.proxyPort system properties:

groovy -Dhttp.proxyHost=yourproxy -Dhttp.proxyPort=8080 yourscript.groovy

Or you can make this system-wide by adding these properties to yourJAVA_OPTS environment variable:

JAVA_OPTS = -Dhttp.proxyHost=yourproxy -Dhttp.proxyPort=8080
Logging

If you want to see what Grape is doing set the system propertygroovy.grape.report.downloads totrue (e.g. add-Dgroovy.grape.report.downloads=true to invocation or JAVA_OPTS) and Grape willprint the following infos to System.error:

  • Starting resolve of a dependency

  • Starting download of an artifact

  • Retrying download of an artifact

  • Download size and time for downloaded artifacts

To log with even more verbosity, increase the Ivy log level(defaults to-1). For example-Divy.message.logger.level=4.

3.5.2. Detail

Grape (TheGroovy Adaptable Packaging Engine orGroovy AdvancedPackaging Engine) is the infrastructure enabling the grab() calls inGroovy, a set of classes leveragingIvy to allow for a repository drivenmodule system for Groovy. This allows a developer to write a script withan essentially arbitrary library requirement, and ship just the script.Grape will, at runtime, download as needed and link the named librariesand all dependencies forming a transitive closure when the script is runfrom existing repositories such as Maven Central.

Grape follows the Ivy conventions for module version identification,with naming change.

  • group - Which module group the module comes from. Translatesdirectly to a Maven groupId or an Ivy Organization. Any group matching/groovy[x][\..*]^/ is reserved and may have special meaning to thegroovy endorsed modules.

  • module - The name of the module to load. Translated directly to aMaven artifactId or an Ivy artifact.

  • version - The version of the module to use. Either a literal version`1.1-RC3' or an Ivy Range `[2.2.1,)' meaning 2.2.1 or any greaterversion).

  • classifier - The optional classifier to use (for example,jdk15)

The downloaded modules will be stored according to Ivy’s standardmechanism with a cache root of~/.groovy/grapes

3.5.3. Usage

Annotation

One or moregroovy.lang.Grab annotations can be added at any place thatannotations are accepted to tell the compiler that this code relies onthe specific library. This will have the effect of adding the library tothe classloader of the groovy compiler. This annotation is detected andevaluated before any other resolution of classes in the script, soimported classes can be properly resolved by a@Grab annotation.

import com.jidesoft.swing.JideSplitButton@Grab(group='com.jidesoft', module='jide-oss', version='[2.2.1,2.3.0)')public class TestClassAnnotation {    public static String testMethod () {        return JideSplitButton.class.name    }}

An appropriategrab(…​) call will be added to the static initializerof the class of the containing class (or script class in the case of anannotated script element).

Multiple Grape Annotations

In early versions of Groovy, if you wanted to use a Grab annotation multiple timeson the same node you had to use the@Grapes annotation, e.g.:

@Grapes([   @Grab(group='commons-primitives', module='commons-primitives', version='1.0'),   @Grab(group='org.ccil.cowan.tagsoup', module='tagsoup', version='0.9.7')])class Example {// ...}

Otherwise you’d encounter the following error:

Cannot specify duplicate annotation on the same member

But in recent versions, @Grapes is purely optional.

Technical notes:

  • Originally, Groovy stored the Grab annotations for access at runtimeand duplicates aren’t allowed in the bytecode. In current versions, @Grab has onlySOURCE retention, so the multiple occurrences aren’t an issue.

  • Future versions of Grape may support using the Grapes annotation toprovide a level of structuring, e.g. allowing a GrabExclude or GrabResolverannotation to apply to only a subset of the Grab annotations.

Method call

Typically a call to grab will occur early in the script or in classinitialization. This is to ensure that the libraries are made availableto the ClassLoader before the groovy code relies on the code. A coupleof typical calls may appear as follows:

import groovy.grape.Grape// random maven libraryGrape.grab(group:'com.jidesoft', module:'jide-oss', version:'[2.2.0,)')Grape.grab([group:'org.apache.ivy', module:'ivy', version:'2.0.0-beta1', conf:['default', 'optional']],     [group:'org.apache.ant', module:'ant', version:'1.7.0'])
  • Multiple calls to grab in the same context with the same parametersshould be idempotent. However, if the same code is called with adifferentClassLoader context then resolution may be re-run.

  • If theargs map passed into thegrab call has an attributenoExceptions that evaluates true no exceptions will be thrown.

  • grab requires that aRootLoader orGroovyClassLoader be specified orbe in theClassLoader chain of the calling class. By default failure tohave such aClassLoader available will result in module resolution andan exception being thrown

    • The ClassLoader passed in via theclassLoader: argument and itsparent classloaders.

    • The ClassLoader of the object passed in as thereferenceObject:argument, and its parent classloaders.

    • The ClassLoader of the class issuing the call tograb

grab(HashMap) Parameters
  • group: - <String> - Which module group the module comes from.Translates directly to a Maven groupId. Any group matching/groovy(|\..|x|x\..)/ is reserved and may have special meaning to thegroovy endorsed modules.

  • module: - <String> - The name of the module to load. Translateddirectly to a Maven artifactId.

  • version: - <String> and possibly <Range> - The version of the moduleto use. Either a literal version `1.1-RC3' or an Ivy Range `[2.2.1,)'meaning 2.2.1 or any greater version).

  • classifier: - <String> - The Maven classifier to resolve by.

  • conf: - <String>, defaultdefault' - The configuration or scope ofthe module to download. The default conf is `default: which maps to themavenruntime andmaster scopes.

  • force:- <boolean>, defaults true - Used to indicate that thisrevision must be used in case of conflicts, independently of

  • conflicts manager

  • changing: - <boolean>, default false - Whether the artifact canchange without its version designation changing.

  • transitive: - <boolean>, default true - Whether to resolve otherdependencies this module has or not.

There are two principal variants ofgrab, one with a single Map andone with an arguments Map and multiple dependencies map. A call to thesingle map grab is the same as calling grab with the same map passed intwice, so grab arguments and dependencies can be mixed in the same map,and grab can be called as a single method with named parameters.

There are synonyms for these parameters. Submitting more than one is aruntime exception.

  • group:,groupId:,organisation:,organization:,org:

  • module:,artifactId:,artifact:

  • version:,revision:,rev:

  • conf:,scope:,configuration:

Arguments Map arguments
  • classLoader: - <GroovyClassLoader> or <RootClassLoader> - TheClassLoader to add resolved Jars to

  • refObject: - <Object> - The closest parent ClassLoader for theobject’s class will be treated as though it were passed in asclassLoader:

  • validate: - <boolean>, default false - Should poms or ivy files bevalidated (true), or should we trust the cache (false).

  • noExceptions: - <boolean>, default false - If ClassLoader resolutionor repository querying fails, should we throw an exception (false) orfail silently (true).

Command Line Tool

Grape added a command line executable `grape' that allows for theinspection and management of the local grape cache.

grape install [-hv] <group> <module> [<version>] [<classifier>]

This installs the specified groovy module or maven artifact. If aversion is specified that specific version will be installed, otherwisethe most recent version will be used (as if `*' we passed in).

grape list

Lists locally installed modules (with their full maven name in the caseof groovy modules) and versions.

grape resolve [-adhisv] (<groupId> <artifactId> <version>)+

This returns the file locations of the jars representing the artifactsfor the specified module(s) and the respective transitive dependencies.You may optionally pass in -ant, -dos, or -shell to get the dependenciesexpressed in a format applicable for an ant script, windows batch file,or unix shell script respectively. -ivy may be passed to see thedependencies expressed in an ivy like format.

grape uninstall [-hv] <group> <module> <version>

This uninstalls a particular grape: it non-transitively removes therespective jar file from the grape cache.

Advanced configuration
Repository Directory

If you need to change the directory grape uses for downloading librariesyou can specify the grape.root system property to change the default(which is ~/.groovy/grapes)

groovy -Dgrape.root=/repo/grapes yourscript.groovy
Customize Ivy settings

You can customize the ivy settings that Grape uses by creating a~/.groovy/grapeConfig.xml file. If no such file exists,hereare the default settings used by Grape.

For more information on how to customize these settings, please refer totheIvydocumentation.

More Examples

Using Apache Commons Collections:

// create and use a primitive array list@Grab(group='commons-primitives', module='commons-primitives', version='1.0')import org.apache.commons.collections.primitives.ArrayIntListdef createEmptyInts() { new ArrayIntList() }def ints = createEmptyInts()ints.add(0, 42)assert ints.size() == 1assert ints.get(0) == 42

Using TagSoup:

// find the PDF links of the Java specifications@Grab(group='org.ccil.cowan.tagsoup', module='tagsoup', version='1.2.1')def getHtml() {    def parser = new XmlParser(new org.ccil.cowan.tagsoup.Parser())    parser.parse("https://docs.oracle.com/javase/specs/")}html.body.'**'.a.@href.grep(~/.*\.pdf/).each{ println it }

Using Google Collections:

import com.google.common.collect.HashBiMap@Grab(group='com.google.code.google-collections', module='google-collect', version='snapshot-20080530')def getFruit() { [grape:'purple', lemon:'yellow', orange:'orange'] as HashBiMap }assert fruit.lemon == 'yellow'assert fruit.inverse().yellow == 'lemon'

Launching a Jetty server to serve Groovy templates:

@Grab('org.eclipse.jetty.aggregate:jetty-server:8.1.19.v20160209')@Grab('org.eclipse.jetty.aggregate:jetty-servlet:8.1.19.v20160209')@Grab('javax.servlet:javax.servlet-api:3.0.1')import org.eclipse.jetty.server.Serverimport org.eclipse.jetty.servlet.ServletContextHandlerimport groovy.servlet.TemplateServletdef runServer(duration) {    def server = new Server(8080)    def context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS)    context.resourceBase = "."    context.addServlet(TemplateServlet, "*.gsp")    server.start()    sleep duration    server.stop()}runServer(10000)

Grape will download Jetty and its dependencies on first launch of thisscript, and cache them. We create a new Jetty Server on port 8080,then expose Groovy’s TemplateServlet at the root of the context — Groovycomes with its own powerful template engine mechanism. We start theserver and let it run for a certain duration. Each time someone will hithttp://localhost:8080/somepage.gsp, it will display the somepage.gsptemplate to the user — those template pages should be situated in thesame directory as this server script.

3.6. Testing Guide

3.6.1. Introduction

The Groovy programming language comes with great support for writing tests. In addition to the languagefeatures and test integration with state-of-the-art testing libraries and frameworks, the Groovy ecosystem has borna rich set of testing libraries and frameworks.

This chapter will start with language specific testing features and continue with a closer look at JUnitintegration, Spock for specifications, and Geb for functional tests. Finally, we’ll do an overview of other testinglibraries known to be working with Groovy.

3.6.2. Language Features

Besides integrated support for JUnit, the Groovy programming language comes with features that have provento be very valuable for test-driven development. This section gives insight on them.

Power Assertions

Writing tests means formulating assumptions by using assertions. In Java this can be done by using theassertkeyword that has been added in J2SE 1.4. In Java,assert statements can be enabled via the JVM parameters-ea(or-enableassertions) and-da (or-disableassertions). Assertion statements in Java are disabled by default.

Groovy comes with a ratherpowerful variant ofassert also known aspower assertion statement. Groovy’s powerassert differs from the Java version in its output given the boolean expression validates tofalse:

def x = 1assert x == 2// Output:(1)//// Assertion failed:// assert x == 2//        | |//        1 false
1This section shows the std-err output

Thejava.lang.AssertionError that is thrown whenever the assertion can not be validated successfully, containsan extended version of the original exception message. The power assertion output shows evaluation results fromthe outer to the inner expression.

The power assertion statements true power unleashes in complex Boolean statements, or statements withcollections or othertoString-enabled classes:

def x = [1,2,3,4,5]assert (x << 6) == [6,7,8,9,10]// Output://// Assertion failed:// assert (x << 6) == [6,7,8,9,10]//         | |     |//         | |     false//         | [1, 2, 3, 4, 5, 6]//         [1, 2, 3, 4, 5, 6]

Another important difference from Java is that in Groovy assertions areenabled by default. It has been a language designdecision to remove the possibility to deactivate assertions. Or, as Bertrand Meyer stated,it makes no sense to takeoff your swim ring if you put your feet into real water.

One thing to be aware of are methods with side effects inside Boolean expressions in power assertion statements. Asthe internal error message construction mechanism does only store references to instances under target, it happens that the error message text is invalid at rendering time in case of side-effecting methods involved:

assert [[1,2,3,3,3,3,4]].first().unique() == [1,2,3]// Output://// Assertion failed:// assert [[1,2,3,3,3,3,4]].first().unique() == [1,2,3]//                          |       |        |//                          |       |        false//                          |       [1, 2, 3, 4]//                          [1, 2, 3, 4](1)
1The error message shows the actual state of the collection, not the state before theunique method was applied
If you choose to provide a custom assertion error message this can be done by using the Java syntaxassertexpression1 : expression2 whereexpression1 is the Boolean expression andexpression2 is the custom error message. Be aware though that this will disable the power assert and will fully fall back to custom error messages on assertion errors.
Mocking and Stubbing

Groovy has excellent built-in support for a range of mocking and stubbing alternatives. When using Java, dynamic mockingframeworks are very popular. A key reason for this is that it is hard work creating custom hand-crafted mocks using Java.Such frameworks can be used easily with Groovy if you choose but creating custom mocks is much easier in Groovy. Youcan often get away with simple maps or closures to build your custom mocks.

The following sections show ways to create mocks and stubs with Groovy language features only.

Map Coercion

By using maps or expandos, we can incorporate desired behaviour of a collaborator very easily as shown here:

class TranslationService {    String convert(String key) {        return "test"    }}def service = [convert: { String key -> 'some text' }] as TranslationServiceassert 'some text' == service.convert('key.text')

Theas operator can be used to coerce a map to a particular class. The given map keys are interpreted asmethod names and the values, beinggroovy.lang.Closure blocks, are interpreted as method code blocks.

Be aware that map coercion can get into the way if you deal with customjava.util.Map descendant classes in combinationwith theas operator. The map coercion mechanism is targeted directly at certain collection classes, it doesn’t takecustom classes into account.
Closure Coercion

The 'as' operator can be used with closures in a neat way which is great for developer testing in simple scenarios.We haven’t found this technique to be so powerful that we want to do away with dynamic mocking, but it can be veryuseful in simple cases none-the-less.

Classes or interfaces holding a single method, including SAM (single abstract method) classes, can be used to coercea closure block to be an object of the given type. Be aware that for doing this, Groovy internally create a proxy objectdescending for the given class. So the object will not be a direct instance of the given class. This important if, forexample, the generated proxy object’s metaclass is altered afterwards.

Let’s have an example on coercing a closure to be of a specific type:

def service = { String key -> 'some text' } as TranslationServiceassert 'some text' == service.convert('key.text')

Groovy supports a feature called implicit SAM coercion. This means that theas operator is not necessary in situationswhere the runtime can infer the target SAM type. This type of coercion might be useful in tests to mock entire SAMclasses:

abstract class BaseService {    abstract void doSomething()}BaseService service = { -> println 'doing something' }service.doSomething()
MockFor and StubFor

The Groovy mocking and stubbing classes can be found in thegroovy.mock.interceptor package.

TheMockFor class supports (typically unit) testing of classes in isolation by allowing astrictly ordered expectationof the behavior of collaborators to be defined. A typical test scenario involves a class under test and one or more collaborators. In such a scenario it isoften desirable to just test the business logic of the class under test. One strategy for doing that is to replacethe collaborator instances with simplified mock objects to help isolate out the logic in the test target. MockForallows such mocks to be created using meta-programming. The desired behavior of collaborators is defined as a behaviorspecification. The behavior is enforced and checked automatically.

Let’s assume our target classes looked like this:

class Person {    String first, last}class Family {    Person father, mother    def nameOfMother() { "$mother.first $mother.last" }}

WithMockFor, a mock expectation is always sequence dependent and its use automatically ends with a call toverify:

def mock = new MockFor(Person)(1)mock.demand.getFirst{ 'dummy' }mock.demand.getLast{ 'name' }mock.use {(2)    def mary = new Person(first:'Mary', last:'Smith')    def f = new Family(mother:mary)    assert f.nameOfMother() == 'dummy name'}mock.expect.verify()(3)
1a new mock is created by a new instance ofMockFor
2aClosure is passed touse which enables the mocking functionality
3a call toverify checks whether the sequence and number of method calls is as expected

TheStubFor class supports (typically unit) testing of classes in isolation by allowing aloosely-ordered expectationof the behavior of collaborators to be defined. A typical test scenario involves a class under test and one or morecollaborators. In such a scenario it is often desirable to just test the business logic of the CUT. One strategy fordoing that is to replace the collaborator instances with simplified stub objects to help isolate out the logicin the target class.StubFor allows such stubs to be created using meta-programming. The desired behavior ofcollaborators is defined as a behavior specification.

In contrast toMockFor the stub expectation checked withverify is sequence independent and its use is optional:

def stub = new StubFor(Person)(1)stub.demand.with {(2)    getLast{ 'name' }    getFirst{ 'dummy' }}stub.use {(3)    def john = new Person(first:'John', last:'Smith')    def f = new Family(father:john)    assert f.father.first == 'dummy'    assert f.father.last == 'name'}stub.expect.verify()(4)
1a new stub is created by a new instance ofStubFor
2thewith method is used for delegating all calls inside the closure to theStubFor instance
3aClosure is passed touse which enables the stubbing functionality
4a call toverify (optional) checks whether the number of method calls is as expected

MockFor andStubFor can not be used to test statically compiled classes e.g. for Java classes or Groovy classes thatmake use of@CompileStatic. To stub and/or mock these classes you can use Spock or one of the Java mocking libraries.

Expando Meta-Class (EMC)

Groovy includes a specialMetaClass the so-calledExpandoMetaClass (EMC). It allows to dynamically add methods,constructors, properties and static methods using a neat closure syntax.

Everyjava.lang.Class is supplied with a specialmetaClass property that will give a reference to anExpandoMetaClass instance. The expando metaclass is not restricted to custom classes, it can be used forJDK classes like for examplejava.lang.String as well:

String.metaClass.swapCase = {->    def sb = new StringBuffer()    delegate.each {        sb << (Character.isUpperCase(it as char) ? Character.toLowerCase(it as char) :            Character.toUpperCase(it as char))    }    sb.toString()}def s = "heLLo, worLD!"assert s.swapCase() == 'HEllO, WORld!'

TheExpandoMetaClass is a rather good candidate for mocking functionality as it allows for more advanced stufflike mocking static methods

class Book {    String title}Book.metaClass.static.create << { String title -> new Book(title:title) }def b = Book.create("The Stand")assert b.title == 'The Stand'

or even constructors

Book.metaClass.constructor << { String title -> new Book(title:title) }def b = new Book("The Stand")assert b.title == 'The Stand'
Mocking constructors might seem like a hack that’s better not even to be considered but even there might be validuse cases. An example can be found in Grails where domain class constructors are added at run-time with thehelp ofExpandoMetaClass. This lets the domain object register itself in the Spring application context and allowsfor injection of services or other beans controlled by the dependency-injection container.

If you want to change themetaClass property on a per test method level you need to remove the changes that weredone to the metaclass, otherwise those changes would be persistent across test method calls. Changes are removed byreplacing the metaclass in theGroovyMetaClassRegistry:

GroovySystem.metaClassRegistry.removeMetaClass(String)

Another alternative is to register aMetaClassRegistryChangeEventListener, track the changed classes and removethe changes in the cleanup method of your chosen testing runtime. A good example can be foundin the Grails webdevelopment framework.

Besides using theExpandoMetaClass on a class-level, there is also support for using the metaclass on a per-objectlevel:

def b = new Book(title: "The Stand")b.metaClass.getTitle {-> 'My Title' }assert b.title == 'My Title'

In this case the metaclass change is related to the instance only. Depending on the test scenario this might be a betterfit than the global metaclass change.

GDK Methods

The following section gives a brief overview on GDK methods that can be leveraged in test case scenarios, for example fortest data generation.

Iterable#combinations

Thecombinations method that is added onjava.lang.Iterable compliant classes can be used to get a list ofcombinations from a list containing two or more sub-lists:

void testCombinations() {    def combinations = [[2, 3],[4, 5, 6]].combinations()    assert combinations == [[2, 4], [3, 4], [2, 5], [3, 5], [2, 6], [3, 6]]}

The method could be used in test case scenarios to generate all possible argument combinations for a specific methodcall.

Iterable#eachCombination

TheeachCombination method that is added onjava.lang.Iterable can be used to apply a function (or in this case agroovy.lang.Closure) to each if the combinations that has been built by thecombinations method:

eachCombination is a GDK method that is added to all classes conforming to thejava.lang.Iterable interface.It applies a function on each combination of the input lists:

void testEachCombination() {    [[2, 3],[4, 5, 6]].eachCombination { println it[0] + it[1] }}

The method could be used in the testing context to call methods with each of the generated combinations.

Tool Support
Test Code Coverage

Code coverage is a useful measure of the effectiveness of (unit) tests. A program with high code coverage has alower chance to hold critical bugs than a program with no or low coverage. To get code coverage metrics,the generated byte-code usually needs to be instrumented before the tests are executed. One tool with Groovy support for this task isCobertura.

The following code listing shows an example on how to enable Cobertura test coverage reports in a Gradle build script froma Groovy project:

def pluginVersion = '<plugin version>'def groovyVersion = '<groovy version>'def junitVersion = '<junit version>'buildscript {    repositories {        mavenCentral()    }    dependencies {        classpath 'com.eriwen:gradle-cobertura-plugin:${pluginVersion}'    }}apply plugin: 'groovy'apply plugin: 'cobertura'repositories {    mavenCentral()}dependencies {    compile "org.codehaus.groovy:groovy-all:${groovyVersion}"    testCompile "junit:junit:${junitVersion}"}cobertura {    format = 'html'    includes = ['**/*.java', '**/*.groovy']    excludes = ['com/thirdparty/**/*.*']}

Several output formats can be chosen for Cobertura coverage reports and test code coverage reports can be added tocontinuous integration build tasks.

3.6.3. Testing with JUnit

Groovy simplifies JUnit testing in the following ways:

  • You use the same overall practices as you would when testing with Java but youcan adopt much of Groovy’s concise syntax in your tests making them succinct. You can even usethe capabilities for writing testing domain specific languages (DSLs) if you feel so inclined.

  • There are numerous helper classes that simplify many testing activities. The details differin some cases depending on the version of JUnit you are using. We’ll cover those details shortly.

  • Groovy’s PowerAssert mechanism is wonderful to use in your tests

  • Groovy deems that tests are so important you should be able to run them as easily as scripts or classes.This is why Groovy includes an automatic test runner when using thegroovy command or the GroovyConsole.This gives you some additional options over and above running your tests

In the following sections we will have a closer look at JUnit 3, 4 and 5 Groovy integration.

JUnit 3

Maybe one of the most prominent Groovy classes supporting JUnit 3 tests is theGroovyTestCase class. Beingderived fromjunit.framework.TestCase it offers a bunch of additional methods that make testing in Groovy a breeze.

AlthoughGroovyTestCase inherits fromTestCase doesn’t mean you can’t use JUnit 4 features in your project. In fact,the most recent Groovy versions come with a bundled JUnit 4 and that comes with a backwards compatibleTestCaseimplementation. There have been some discussion on the Groovy mailing-list on whether to useGroovyTestCase or JUnit 4with the result that it is mostly a matter of taste, but withGroovyTestCase you get a bunch of methods for free thatmake certain types of tests easier to write.

In this section, we will have a look at some of the methods provided byGroovyTestCase. A full list of these can befound in the JavaDoc documentation forgroovy.test.GroovyTestCase ,don’t forget it is inherited fromjunit.framework.TestCase which inherits all theassert* methods.

Assertion Methods

GroovyTestCase is inherited fromjunit.framework.TestCase therefore it inherits a large number of assertion methodsbeing available to be called in every test method:

class MyTestCase extends GroovyTestCase {    void testAssertions() {        assertTrue(1 == 1)        assertEquals("test", "test")        def x = "42"        assertNotNull "x must not be null", x        assertNull null        assertSame x, x    }}

As can be seen above, in contrast to Java it is possible to leave out the parenthesis in most situations whichleads to even more readability of JUnit assertion method call expressions.

An interesting assertion method that is added byGroovyTestCase isassertScript. It ensures that the given Groovycode string succeeds without any exception:

void testScriptAssertions() {    assertScript '''        def x = 1        def y = 2        assert x + y == 3    '''}
shouldFail Methods

shouldFail can be used to check whether the given code block fails or not. In case it fails, the assertion does hold,otherwise the assertion fails:

void testInvalidIndexAccess1() {    def numbers = [1,2,3,4]    shouldFail {        numbers.get(4)    }}

The example above uses the basicshouldFail method interface that takes agroovy.lang.Closure as a single argument. TheClosure instance holds the code that is supposed to be breaking during run-time.

If we wanted to assertshouldFail on a specificjava.lang.Exception type we could have done so by using theshouldFailimplementation that takes theException class as first argument and theClosure as second argument:

void testInvalidIndexAccess2() {    def numbers = [1,2,3,4]    shouldFail IndexOutOfBoundsException, {        numbers.get(4)    }}

If anything other thanIndexOutOfBoundsException (or a descendant class of it) is thrown, the test case will fail.

A pretty nice feature ofshouldFail hasn’t been visible so far: it returns the exception message. This is reallyuseful if you want to assert on the exception error message:

void testInvalidIndexAccess3() {    def numbers = [1,2,3,4]    def msg = shouldFail IndexOutOfBoundsException, {        numbers.get(4)    }    assert msg.contains('Index: 4, Size: 4') ||        msg.contains('Index 4 out-of-bounds for length 4') ||        msg.contains('Index 4 out of bounds for length 4')}
notYetImplemented Method

ThenotYetImplemented method has been greatly influenced by HtmlUnit. It allows to write a test method but mark itas not yet implemented. As long as the test method fails and is marked withnotYetImplemented the test goes green:

void testNotYetImplemented1() {    if (notYetImplemented()) return(1)    assert 1 == 2(2)}
1a call tonotYetImplemented is necessary forGroovyTestCase to get the current method stack.
2as long as the test evaluates tofalse the test execution will be successful.

An alternative to thenotYetImplemented method is the@NotYetImplemented annotation. It allows for annotating amethod as not yet implemented, with the exact same behavior asGroovyTestCase#notYetImplemented but without the needfor thenotYetImplemented method call:

@NotYetImplementedvoid testNotYetImplemented2() {    assert 1 == 2}
JUnit 4

Groovy can be used to write JUnit 4 test cases without any restrictions. Thegroovy.test.GroovyAssert holdsvarious static methods that can be used as replacement for theGroovyTestCase methods in JUnit 4 tests:

import org.junit.Testimport static groovy.test.GroovyAssert.shouldFailclass JUnit4ExampleTests {    @Test    void indexOutOfBoundsAccess() {        def numbers = [1,2,3,4]        shouldFail {            numbers.get(4)        }    }}

As can be seen in the example above, the static methods found inGroovyAssert are imported at the beginning of the class definition thusshouldFail can be used the same way it can be used in aGroovyTestCase.

groovy.test.GroovyAssert descends fromorg.junit.Assert that means it inherits all JUnit assertion methods. However,with the introduction of the power assertion statement, it turned out to begood practice to rely on assertion statementsinstead of using the JUnit assertion methods with the improved message being the main reason.

It is worth mentioning thatGroovyAssert.shouldFail is not absolutely identical toGroovyTestCase.shouldFail. WhileGroovyTestCase.shouldFail returns the exception message,GroovyAssert.shouldFail returns the exception itself. Ittakes a few more keystrokes to get the message, but in return you can access other properties and methods of theexception:

@Testvoid shouldFailReturn() {    def e = shouldFail {        throw new RuntimeException('foo',                                   new RuntimeException('bar'))    }    assert e instanceof RuntimeException    assert e.message == 'foo'    assert e.cause.message == 'bar'}
JUnit 5

Much of the approach and helper classes described under JUnit4 apply when using JUnit5 however JUnit5uses some slightly different class annotations when writing your tests. See theJUnit5 documentationfor more details.

Create your test classes as per normal JUnit5 guidelines as shown in this example:

class MyTest {  @Test  void streamSum() {    assertTrue(Stream.of(1, 2, 3)      .mapToInt(i -> i)      .sum() > 5, () -> "Sum should be greater than 5")  }  @RepeatedTest(value=2, name = "{displayName} {currentRepetition}/{totalRepetitions}")  void streamSumRepeated() {    assert Stream.of(1, 2, 3).mapToInt(i -> i).sum() == 6  }  private boolean isPalindrome(s) { s == s.reverse()  }  @ParameterizedTest(1)  @ValueSource(strings = [ "racecar", "radar", "able was I ere I saw elba" ])  void palindromes(String candidate) {    assert isPalindrome(candidate)  }  @TestFactory  def dynamicTestCollection() {[    dynamicTest("Add test") { -> assert 1 + 1 == 2 },    dynamicTest("Multiply Test", () -> { assert 2 * 3 == 6 })  ]}}
1This test requires the additionalorg.junit.jupiter:junit-jupiter-params dependency if not already in your project.

You can run the tests in your IDE or build tool if it supports and is configured for JUnit5.If you run the above test in the GroovyConsole or via thegroovy command, you will see a short text summary of theresult of running the test:

JUnit5 launcher: passed=8, failed=0, skipped=0, time=246ms

More detailed information is available at theFINE logging level. You can configure your logging to display suchinformation or do it programmatically as follows:

@BeforeAllstatic void init() {  def logger = Logger.getLogger(LoggingListener.name)  logger.level = Level.FINE  logger.addHandler(new ConsoleHandler(level: Level.FINE))}

3.6.4. Testing with Spock

Spock is a testing and specification framework for Java and Groovy applications. What makes it stand out from thecrowd is its beautiful and highly expressive specification DSL. In practice, Spock specifications are written asGroovy classes. Although written in Groovy they can be used to test Java classes. Spock can be used for unit,integration or BDD (behavior-driven-development) testing, it doesn’t put itself into a specific category of testingframeworks or libraries.

Beside these awesome features Spock is a good example on how to leverage advanced Groovy programminglanguage features in third party libraries, for example, by using Groovy AST transformations.
This section should not serve as detailed guide on how to use Spock, it should rather give an impression what Spockis about and how it can be leveraged for unit, integration, functional or any other type of testing.

The next section we will have a first look at the anatomy of a Spock specification. It should give apretty good feeling on what Spock is up to.

Specifications

Spock lets you write specifications that describe features (properties, aspects) exhibited by a system ofinterest. The "system" can be anything between a single class and an entire application, a more advanced term for it issystem under specification. Thefeature description starts from a specific snapshot of the system and itscollaborators, this snapshot is called thefeature’s fixture.

Spock specification classes are derived fromspock.lang.Specification. A concrete specification class might consistof fields, fixture methods, features methods and helper methods.

Let’s have a look at a simple specification with a single feature method for an imaginaryStack class:

class StackSpec extends Specification {    def "adding an element leads to size increase"() {(1)        setup: "a new stack instance is created"(2)            def stack = new Stack()        when:(3)            stack.push 42        then:(4)            stack.size() == 1    }}
1Feature method, is by convention named with a String literal.
2Setup block, here is where any setup work for this feature needs to be done.
3When block describes a stimulus, a certain action under target by this feature specification.
4Then block any expressions that can be used to validate the result of the code that was triggered by the when block.

Spock feature specifications are defined as methods inside aspock.lang.Specification class. They describe the featureby using a String literal instead of a method name.

A feature method holds multiple blocks, in our example we usedsetup,when andthen. Thesetup block is specialin that it is optional and allows to configure local variables visible inside the feature method. Thewhen blockdefines the stimulus and is a companion of thethen block which describes the response to the stimulus.

Note that thesetup method in theStackSpec above additionally has a description String. Description Strings are optional and can be added after block labels (likesetup,when,then).

More Spock

Spock provides much more features like data tables or advanced mocking capabilities. Feel free to consult theSpock GitHub page for more documentation and download information.

3.6.5. Functional Tests with Geb

Geb is a functional web testing and scraper library that integrates with JUnit and Spock. It is based upon theSelenium web drivers and, like Spock, provides a Groovy DSL to write functional tests for web applications.

Geb has great features that make it a good fit for a functional testing library:

  • DOM access via a JQuery-like$ function

  • implements thepage pattern

  • support for modularization of certain web components (e.g. menu-bars, etc.) withmodules

  • integration with JavaScript via the JS variable

This section should not serve as detailed guide on how to use Geb, it should rather give an impression what Gebis about and how it can be leveraged functional testing.

The next section will give an example on how Geb can be used to write a functional test for a simple web page with a single search field.

A Geb Script

Although Geb can be used standalone in a Groovy script, in many scenarios it’s used in combination with other testingframeworks. Geb comes with various base classes that can be used in JUnit 3, 4, TestNG or Spock tests. The base classesare part of additional Geb modules that need to be added as a dependency.

For example, the following@Grab dependencies can be used to run Geb with the Selenium Firefox driver inJUnit4 tests. The module that is needed for JUnit 3/4 support isgeb-junit4:

@Grab('org.gebish:geb-core:0.9.2')@Grab('org.gebish:geb-junit4:0.9.2')@Grab('org.seleniumhq.selenium:selenium-firefox-driver:2.26.0')@Grab('org.seleniumhq.selenium:selenium-support:2.26.0')

The central class in Geb is thegeb.Browser class. As its name implies it is used to browse pages and access DOM elements:

import geb.Browserimport org.openqa.selenium.firefox.FirefoxDriverdef browser = new Browser(driver: new FirefoxDriver(), baseUrl: 'http://myhost:8080/myapp')(1)browser.drive {    go "/login"(2)    $("#username").text = 'John'(3)    $("#password").text = 'Doe'    $("#loginButton").click()    assert title == "My Application - Dashboard"}
1A newBrowser instance is created. In this case it uses the SeleniumFirefoxDriver and sets thebaseUrl.
2go is used to navigate to a URL or relative URI
3$ together with CSS selectors is used to access theusername andpassword DOM fields.

TheBrowser class comes with adrive method that delegates all method/property calls to the currentbrowser instance. TheBrowser configuration must not be done inline, it can also be externalized in aGebConfig.groovy configuration file for example. In practice, the usage of theBrowser class is mostly hiddenby Geb test base classes. They delegate all missing properties and method calls to the currentbrowser instancethat exists in the background:

class SearchTests extends geb.junit4.GebTest {    @Test    void executeSeach() {        go 'http://somehost/mayapp/search'(1)        $('#searchField').text = 'John Doe'(2)        $('#searchButton').click()(3)        assert $('.searchResult a').first().text() == 'Mr. John Doe'(4)    }}
1Browser#go takes a relative or absolute link and calls the page.
2Browser#$ is used to access DOM content. Any CSS selectors supported by the underlying Selenium drivers are allowed
3click is used to click a button.
4$ is used to get the first link out of thesearchResult block

The example above shows a simple Geb web test with the JUnit 4 base classgeb.junit4.GebTest. Note that in this casetheBrowser configuration is externalized.GebTest delegates methods likego and$ to the underlyingbrowser instance.

More Geb

In the previous section we only scratched the surface of the available Geb features. More information on Geb can be foundat theproject homepage.

3.7. Tune parsing performance of Parrot parser

The Parrot parser is based on antlr4 and introduced since Groovy 3.0.0. It provides the following options to tune parsing performance:

OptionDescriptionDefaultVersionExample

groovy.parallel.parse

Parsing groovy source files in parallel.

false util Groovy 4.0.0

3.0.5+

-Dgroovy.parallel.parse=true

groovy.antlr4.cache.threshold

antlr4 relies on DFA cache heavily for better performance, so antlr4 will not clear DFA cache, thus OutOfMemoryError will probably occur. Groovy trades off parsing performance and memory usage, when the count of Groovy source files parsed hits the cache threshold, the DFA cache will be cleared.Note:0 means never clearing DFA cache, so requiring bigger JVM heap size. Or set a greater value, e.g. 200 to clear DFA cache if threshold hits.Note: the threshold specified is the count of groovy source files

64

3.0.5+

-Dgroovy.antlr4.cache.threshold=200

groovy.antlr4.sll.threshold

Parrot parser will try SLL mode and then try LL mode if SLL failed. But the more tokens to parse, the more likely SLL will fail. If SLL threshold hits, SLL will be skipped. Setting the threshold to0 means never trying SLL mode, which is not recommended at most cases because SLL is the fastest mode though SLL is less powerful than LL.Note: the threshold specified is the token count

-1 (disabled by default)

3.0.9+

-Dgroovy.antlr4.sll.threshold=1000

groovy.antlr4.clear.lexer.dfa.cache

Clear the DFA cache for lexer. The DFA cache for lexer is always small and important for parsing performance, so it’s strongly recommended to leave it as it is until OutOfMemoryError will truly occur

false

3.0.9+

-Dgroovy.antlr4.clear.lexer.dfa.cache=true

3.8. Processing JSON

Groovy comes with integrated support for converting between Groovy objects and JSON. The classes dedicated toJSON serialisation and parsing are found in thegroovy.json package.

3.8.1. JsonSlurper

JsonSlurper is a class that parses JSON text or reader content into Groovy data structures (objects) such as maps, lists andprimitive types likeInteger,Double,Boolean andString.

The class comes with a bunch of overloadedparse methods plus some special methods such asparseText,parseFile and others. For the next example we will use theparseText method. It parses a JSONString and recursively converts it to alist or map of objects. The otherparse* methods are similar in that they return a JSONString but for different parametertypes.

def jsonSlurper = new JsonSlurper()def object = jsonSlurper.parseText('{ "name": "John Doe" } /* some comment */')assert object instanceof Mapassert object.name == 'John Doe'

Notice the result is a plain map and can be handled like a normal Groovy object instance.JsonSlurper parses thegiven JSON as defined by theECMA-404 JSON Interchange Standardplus support for JavaScript comments and dates.

In addition to mapsJsonSlurper supports JSON arrays which are converted to lists.

def jsonSlurper = new JsonSlurper()def object = jsonSlurper.parseText('{ "myList": [4, 8, 15, 16, 23, 42] }')assert object instanceof Mapassert object.myList instanceof Listassert object.myList == [4, 8, 15, 16, 23, 42]

The JSON standard supports the following primitive data types: string, number, object,true,false andnull.JsonSlurperconverts these JSON types into corresponding Groovy types.

def jsonSlurper = new JsonSlurper()def object = jsonSlurper.parseText '''    { "simple": 123,      "fraction": 123.66,      "exponential": 123e12    }'''assert object instanceof Mapassert object.simple.class == Integerassert object.fraction.class == BigDecimalassert object.exponential.class == BigDecimal

AsJsonSlurper is returning pure Groovy object instances without any special JSON classes in the back, its usageis transparent. In fact,JsonSlurper results conform to GPath expressions. GPath is a powerful expression languagethat is supported by multiple slurpers for different data formats (XmlSlurper for XML being one example).

For more details please have a look at the section onGPath expressions.

The following table gives an overview of the JSON types and the corresponding Groovy data types:

JSONGroovy

string

java.lang.String

number

java.lang.BigDecimal orjava.lang.Integer

object

java.util.LinkedHashMap

array

java.util.ArrayList

true

true

false

false

null

null

date

java.util.Date based on theyyyy-MM-dd’T’HH:mm:ssZ date format

Whenever a value in JSON isnull,JsonSlurper supplements it with the Groovynull value. This is in contrast to otherJSON parsers that represent anull value with a library-provided singleton object.
Parser Variants

JsonSlurper comes with a couple of parser implementations. Each parser fits different requirements, it could well be that for certainscenarios theJsonSlurper default parser is not the best bet for all situations. Here is an overview of the shipped parser implementations:

  • TheJsonParserCharArray parser basically takes a JSON string and operates on the underlying character array. During valueconversion it copies character sub-arrays (a mechanism known as "chopping") and operates on them.

  • TheJsonFastParser is a special variant of theJsonParserCharArray and is the fastest parser. However, it is not thedefault parser for a reason.JsonFastParser is a so-called index-overlay parser. During parsing of the given JSONString ittries as hard as possible to avoid creating new char arrays orString instances. It keeps pointers tothe underlying original character array only. In addition, it defers object creation as late as possible. If parsed maps areput into long-term caches care must be taken as the map objects might not be created and still consist of pointer to theoriginal char buffer only. However,JsonFastParser comes with a special chop mode which dices up the char bufferearly to keep a small copy of the original buffer. Recommendation is to use theJsonFastParser for JSON buffersunder 2MB and keeping the long-term cache restriction in mind.

  • TheJsonParserLax is a special variant of theJsonParserCharArray parser. It has similar performance characteristics asJsonFastParserbut differs in that it isn’t exclusively relying on the ECMA-404 JSON grammar. For example it allows for comments, no quote strings etc.

  • TheJsonParserUsingCharacterSource is a special parser for very large files. It uses a technique called "character windowing" toparse large JSON files (large means files over 2MB size in this case) with constant performance characteristics.

The default parser implementation forJsonSlurper isJsonParserCharArray. TheJsonParserType enumeration contains constants forthe parser implementations described above:

ImplementationConstant

JsonParserCharArray

JsonParserType#CHAR_BUFFER

JsonFastParser

JsonParserType#INDEX_OVERLAY

JsonParserLax

JsonParserType#LAX

JsonParserUsingCharacterSource

JsonParserType#CHARACTER_SOURCE

Changing the parser implementation is as easy as setting theJsonParserType with a call toJsonSlurper#setType().

def jsonSlurper = new JsonSlurper(type: JsonParserType.INDEX_OVERLAY)def object = jsonSlurper.parseText('{ "myList": [4, 8, 15, 16, 23, 42] }')assert object instanceof Mapassert object.myList instanceof Listassert object.myList == [4, 8, 15, 16, 23, 42]

3.8.2. JsonOutput

JsonOutput is responsible for serialising Groovy objects into JSON strings. It can be seen as companion object toJsonSlurper, being a JSON parser.

JsonOutput comes with overloaded, statictoJson methods. EachtoJson implementation takes a different parameter type.The static methods can either be used directly or by importing the methods with a static import statement.

The result of atoJson call is aString containing the JSON code.

def json = JsonOutput.toJson([name: 'John Doe', age: 42])assert json == '{"name":"John Doe","age":42}'

JsonOutput does not only support primitive, maps or list data types to be serialized to JSON, it goes further and evenhas support for serialising POGOs, that is, plain-old Groovy objects.

class Person { String name }def json = JsonOutput.toJson([ new Person(name: 'John'), new Person(name: 'Max') ])assert json == '[{"name":"John"},{"name":"Max"}]'
Customizing Output

If you need control over the serialized output you can use aJsonGenerator. TheJsonGenerator.Options buildercan be used to create a customized generator. One or more options can be set on this builder in order to alterthe resulting output. When you are done setting the options simply call thebuild() method in order to get a fullyconfigured instance that will generate output based on the options selected.

class Person {    String name    String title    int age    String password    Date dob    URL favoriteUrl}Person person = new Person(name: 'John', title: null, age: 21, password: 'secret',                            dob: Date.parse('yyyy-MM-dd', '1984-12-15'),                            favoriteUrl: new URL('http://groovy-lang.org/'))def generator = new JsonGenerator.Options()    .excludeNulls()    .dateFormat('yyyy@MM')    .excludeFieldsByName('age', 'password')    .excludeFieldsByType(URL)    .build()assert generator.toJson(person) == '{"name":"John","dob":"1984@12"}'

A closure can be used to transform a type. These closure converters are registered for a given type and will becalled any time that type or a subtype is encountered. The first parameter to the closure is an object matching thetype for which the converter is registered and this parameter is required. The closure may take an optional secondString parameter and this will be set to the key name if one is available.

class Person {    String name    URL favoriteUrl}Person person = new Person(name: 'John', favoriteUrl: new URL('http://groovy-lang.org/json.html#_jsonoutput'))def generator = new JsonGenerator.Options()    .addConverter(URL) { URL u, String key ->        if (key == 'favoriteUrl') {            u.getHost()        } else {            u        }    }    .build()assert generator.toJson(person) == '{"name":"John","favoriteUrl":"groovy-lang.org"}'// No key available when generating a JSON Arraydef list = [new URL('http://groovy-lang.org/json.html#_jsonoutput')]assert generator.toJson(list) == '["http://groovy-lang.org/json.html#_jsonoutput"]'// First parameter to the converter must match the type for which it is registeredshouldFail(IllegalArgumentException) {    new JsonGenerator.Options()        .addConverter(Date) { Calendar cal -> }}
Formatted Output

As we saw in previous examples, the JSON output is not pretty printed per default. However, theprettyPrint method inJsonOutput comesto rescue for this task.

def json = JsonOutput.toJson([name: 'John Doe', age: 42])assert json == '{"name":"John Doe","age":42}'assert JsonOutput.prettyPrint(json) == '''\{    "name": "John Doe",    "age": 42}'''.stripIndent()

prettyPrint takes aString as single parameter; therefore, it can be applied on arbitrary JSONString instances, not only the result ofJsonOutput.toJson.

Builders

Another way to create JSON from Groovy is to useJsonBuilder orStreamingJsonBuilder. Both builders provide aDSL which allows to formulate an object graph which is then converted to JSON.

For more details on builders, have a look at the builders chapter which covers bothJsonBuilderandStreamingJsonBuilder.

3.9. Interacting with a SQL database

Groovy’sgroovy-sql module provides a higher-level abstraction over Java’s JDBC technology. JDBC itself providesa lower-level but fairly comprehensive API which provides uniform access to a whole variety of supported relational database systems.We’ll use HSQLDB in our examples here but you can alternatively use Oracle, SQL Server, MySQL and a host of others.The most frequently used class within thegroovy-sql module is thegroovy.sql.Sql class which raises the JDBCabstractions up one level. We’ll cover that first.

3.9.1. Connecting to the database

Connecting to a database with Groovy’sSql class requires four pieces of information:

  • The database uniform resource locator (URL)

  • Username

  • Password

  • The driver class name (which can be derived automatically in some situations)

For our HSQLDB database, the values will be something like that shown in the following table:

PropertyValue

url

jdbc:hsqldb:mem:yourdb

user

sa (or yourusername)

password

yourPassword

driver

org.hsqldb.jdbcDriver

Consult the documentation for the JDBC driver that you plan to use to determine the correct values for your situation.

TheSql class has anewInstance factory method which takes these parameters. You would typically use it as follows:

Connecting to HSQLDB
import groovy.sql.Sqldef url = 'jdbc:hsqldb:mem:yourDB'def user = 'sa'def password = ''def driver = 'org.hsqldb.jdbcDriver'def sql = Sql.newInstance(url, user, password, driver)// use 'sql' instance ...sql.close()

If you don’t want to have to handle resource handling yourself (i.e. callclose() manually) then you can use thewithInstance variation as shown here:

Connecting to HSQLDB (withInstance variation)
Sql.withInstance(url, user, password, driver) { sql ->  // use 'sql' instance ...}
Connecting with a DataSource

It is often preferred to use a DataSource. You may have one available to you from a connection pool.Here we’ll use the one provided as part of the HSQLDB driver jar as shown here:

Connecting to HSQLDB with a DataSource
import groovy.sql.Sqlimport org.hsqldb.jdbc.JDBCDataSourcedef dataSource = new JDBCDataSource(    database: 'jdbc:hsqldb:mem:yourDB', user: 'sa', password: '')def sql = new Sql(dataSource)// use then close 'sql' instance ...

If you have your own connection pooling, the details will be different, e.g. for Apache Commons DBCP:

Connecting to HSQLDB with a DataSource using Apache Commons DBCP
@Grab('org.apache.commons:commons-dbcp2:2.7.0')import groovy.sql.Sqlimport org.apache.commons.dbcp2.BasicDataSourcedef ds = new BasicDataSource(driverClassName: "org.hsqldb.jdbcDriver",    url: 'jdbc:hsqldb:mem:yourDB', username: 'sa', password: '')def sql = new Sql(ds)// use then close 'sql' instance ...
Connecting using @Grab

The previous examples assume that the necessary database driver jar is already on your classpath.For a self-contained script you can add@Grab statements to the top of the script to automatically download the necessary jar as shown here:

Connecting to HSQLDB using @Grab
<<<<<<< HEAD        @Grab('org.hsqldb:hsqldb:2.7.2:jdk8')=======        @Grab('org.hsqldb:hsqldb:2.7.3')>>>>>>> 35be169b6c (GROOVY-11418: Bump hsqldb to 2.7.3 (test dependency))        @GrabConfig(systemClassLoader=true)        // create, use, and then close sql instance ...

The@GrabConfig statement is necessary to make sure the system classloader is used. This ensures that the driver classes andsystem classes likejava.sql.DriverManager are in the same classloader.

3.9.2. Executing SQL

You can execute arbitrary SQL commands using theexecute() method. Let’s have a look at using it to create a table.

Creating tables

The simplest way to execute SQL is to call theexecute() method passing the SQL you wish to execute as a String as shown here:

Creating a table
// ... create 'sql' instancesql.execute '''  CREATE TABLE Author (    id          INTEGER GENERATED BY DEFAULT AS IDENTITY,    firstname   VARCHAR(64),    lastname    VARCHAR(64)  );'''// close 'sql' instance ...

There is a variant of this method which takes a GString and another with a list of parameters. There are also other variants with similar names:executeInsert andexecuteUpdate.We’ll see examples of these variants in other examples in this section.

3.9.3. Basic CRUD operations

The basic operations on a database are Create, Read, Update and Delete (the so-called CRUD operations). We’ll examine each of these in turn.

Creating/Inserting data

You can use the sameexecute() statement we saw earlier but to insert a row by using a SQL insert statement as follows:

Inserting a row
sql.execute "INSERT INTO Author (firstname, lastname) VALUES ('Dierk', 'Koenig')"

You can use a specialexecuteInsert method instead ofexecute. This will return a list of all keys generated.Both theexecute andexecuteInsert methods allow you to place '?' placeholders into your SQL string and supply a list of parameters.In this case a PreparedStatement is used which avoids any risk of SQL injection. The following example illustratesexecuteInsert using placeholders and parameters:

Inserting a row using executeInsert with placeholders and parameters
def insertSql = 'INSERT INTO Author (firstname, lastname) VALUES (?,?)'def params = ['Jon', 'Skeet']def keys = sql.executeInsert insertSql, paramsassert keys[0] == [1]

In addition, both theexecute andexecuteInsert methods allow you to use GStrings. Any '$' placeholders within the SQL are assumedto be placeholders. An escaping mechanism exists if you want to supply part of the GString with a variable in aposition which isn’t where normal placeholders go within SQL. See the GroovyDoc for more details.Also,executeInsert allows you to supply a list of key column names, when multiple keys are returned and you are only interested in some of them. Here is a fragment illustrating key name specification and GStrings:

Inserting a row using executeInsert with a GString and specifying key names
def first = 'Guillaume'def last = 'Laforge'def myKeyNames = ['ID']def myKeys = sql.executeInsert """  INSERT INTO Author (firstname, lastname)  VALUES (${first}, ${last})""", myKeyNamesassert myKeys[0] == [ID: 2]
Reading rows

Reading rows of data from the database is accomplished using one of several available methods:query,eachRow,firstRow androws.

Use thequery method if you want to iterate through theResultSet returned by the underlying JDBC API as shown here:

Reading data usingquery
def expected = ['Dierk Koenig', 'Jon Skeet', 'Guillaume Laforge']def rowNum = 0sql.query('SELECT firstname, lastname FROM Author') { resultSet ->  while (resultSet.next()) {    def first = resultSet.getString(1)    def last = resultSet.getString('lastname')    assert expected[rowNum++] == "$first $last"  }}

Use theeachRow method if you want a slightly higher-level abstraction which provides a Groovy friendly map-like abstraction for theResultSet as shown here:

Reading data usingeachRow
rowNum = 0sql.eachRow('SELECT firstname, lastname FROM Author') { row ->  def first = row[0]  def last = row.lastname  assert expected[rowNum++] == "$first $last"}

Note that you can use Groovy list-style and map-style notations when accessing the row of data.

Use thefirstRow method if you for similar functionality aseachRow but returning only one row of data as shown here:

Reading data usingfirstRow
def first = sql.firstRow('SELECT lastname, firstname FROM Author')assert first.values().sort().join(',') == 'Dierk,Koenig'

Use therows method if you want to process a list of map-like data structures as shown here:

Reading data usingrows
List authors = sql.rows('SELECT firstname, lastname FROM Author')assert authors.size() == 3assert authors.collect { "$it.FIRSTNAME ${it[-1]}" } == expected

Note that the map-like abstraction has case-insensitive keys (hence we can use 'FIRSTNAME' or 'firstname' as the key) andalso that -ve indices (a standard Groovy feature) works when using an index value (to count column numbers from the right).

You can also use any of the above methods to return scalar values, though typicallyfirstRow is all that is required in such cases. An example returning the count of rows is shown here:

Reading scalar values
assert sql.firstRow('SELECT COUNT(*) AS num FROM Author').num == 3
Updating rows

Updating rows can again be done using theexecute() method. Just use a SQL update statement as the argument to the method.You can insert an author with just a lastname and then update the row to also have a firstname as follows:

Updating a row
sql.execute "INSERT INTO Author (lastname) VALUES ('Thorvaldsson')"sql.execute "UPDATE Author SET firstname='Erik' where lastname='Thorvaldsson'"

There is also a specialexecuteUpdate variant which returns the number of rows updated as a result of executing the SQL.For example, you can change the lastname of an author as follows:

Using executeUpdate
def updateSql = "UPDATE Author SET lastname='Pragt' where lastname='Thorvaldsson'"def updateCount = sql.executeUpdate updateSqlassert updateCount == 1def row = sql.firstRow "SELECT * FROM Author where firstname = 'Erik'"assert "${row.firstname} ${row.lastname}" == 'Erik Pragt'
Deleting rows

Theexecute method is also used for deleting rows as this example shows:

Deleting rows
assert sql.firstRow('SELECT COUNT(*) as num FROM Author').num == 3sql.execute "DELETE FROM Author WHERE lastname = 'Skeet'"assert sql.firstRow('SELECT COUNT(*) as num FROM Author').num == 2

3.9.4. Advanced SQL operations

Working with transactions

The easiest way to perform database operations within a transaction is to include the database operation within awithTransaction closure as shown in the following example:

A successful transaction
assert sql.firstRow('SELECT COUNT(*) as num FROM Author').num == 0sql.withTransaction {  sql.execute "INSERT INTO Author (firstname, lastname) VALUES ('Dierk', 'Koenig')"  sql.execute "INSERT INTO Author (firstname, lastname) VALUES ('Jon', 'Skeet')"}assert sql.firstRow('SELECT COUNT(*) as num FROM Author').num == 2

Here the database starts empty and has two rows after successful completion of the operation. Outside the scope of thetransaction, the database is never seen as having just one row.

If something goes wrong, any earlier operations within thewithTransaction block are rolled back.We can see that in operation in the following example where we use database metadata (more details coming up shortly) to find themaximum allowable size of thefirstname column and then attempt to enter a firstname one larger than that maximum value as shown here:

A failed transaction will cause a rollback
def maxFirstnameLengthdef metaClosure = { meta -> maxFirstnameLength = meta.getPrecision(1) }def rowClosure = {}def rowCountBefore = sql.firstRow('SELECT COUNT(*) as num FROM Author').numtry {  sql.withTransaction {    sql.execute "INSERT INTO Author (firstname) VALUES ('Dierk')"    sql.eachRow "SELECT firstname FROM Author WHERE firstname = 'Dierk'", metaClosure, rowClosure    sql.execute "INSERT INTO Author (firstname) VALUES (?)", 'X' * (maxFirstnameLength + 1)  }} catch(ignore) { println ignore.message }def rowCountAfter = sql.firstRow('SELECT COUNT(*) as num FROM Author').numassert rowCountBefore == rowCountAfter

Even though the first sql execute succeeds initially, it will be rolled back and the number of rows will remain the same.

Using batches

When dealing with large volumes of data, particularly when inserting such data, it can be more efficient to chunk the data into batches. This is doneusing thewithBatch statement as shown in the following example:

Batching SQL statements
sql.withBatch(3) { stmt ->  stmt.addBatch "INSERT INTO Author (firstname, lastname) VALUES ('Dierk', 'Koenig')"  stmt.addBatch "INSERT INTO Author (firstname, lastname) VALUES ('Paul', 'King')"  stmt.addBatch "INSERT INTO Author (firstname, lastname) VALUES ('Guillaume', 'Laforge')"  stmt.addBatch "INSERT INTO Author (firstname, lastname) VALUES ('Hamlet', 'D''Arcy')"  stmt.addBatch "INSERT INTO Author (firstname, lastname) VALUES ('Cedric', 'Champeau')"  stmt.addBatch "INSERT INTO Author (firstname, lastname) VALUES ('Erik', 'Pragt')"  stmt.addBatch "INSERT INTO Author (firstname, lastname) VALUES ('Jon', 'Skeet')"}

After executing these statements, there will be 7 new rows in the database. In fact, they will have been added in batcheseven though you can’t easily tell that after that fact. If you want to confirm what is going on under the covers, you canadd a little bit of extra logging into your program. Add the following lines before thewithBatch statement:

Logging additional SQL information
import java.util.logging.*// next line will add fine loggingLogger.getLogger('groovy.sql').level = Level.FINE// also adjust logging.properties file in JRE_HOME/lib to have:// java.util.logging.ConsoleHandler.level = FINE

With this extra logging turned on, and the changes made as per the above comment for the logging.properties file, you should seeoutput such as:

SQL logging output with batching enable
FINE: Successfully executed batch with 3 command(s)Apr 19, 2015 8:38:42 PM groovy.sql.BatchingStatementWrapper processResultFINE: Successfully executed batch with 3 command(s)Apr 19, 2015 8:38:42 PM groovy.sql.BatchingStatementWrapper processResultFINE: Successfully executed batch with 1 command(s)Apr 19, 2015 8:38:42 PM groovy.sql.Sql getStatement

We should also note, that any combination of SQL statements can be added to the batch. They don’t all have to beinserting a new row to the same table.

We noted earlier that to avoid SQL injection, we encourage you to use prepared statements, this is achieved using thevariants of methods which take GStrings or a list of extra parameters. Prepared statements can be used in combinationwith batches as shown in the following example:

Batching prepared statements
def qry = 'INSERT INTO Author (firstname, lastname) VALUES (?,?)'sql.withBatch(3, qry) { ps ->  ps.addBatch('Dierk', 'Koenig')  ps.addBatch('Paul', 'King')  ps.addBatch('Guillaume', 'Laforge')  ps.addBatch('Hamlet', "D'Arcy")  ps.addBatch('Cedric', 'Champeau')  ps.addBatch('Erik', 'Pragt')  ps.addBatch('Jon', 'Skeet')}

This provides a much safer option if the data could come from a user such as via a script or a web form. Of course, giventhat a prepared statement is being used, you are limited to a batch of the same SQL operation (insert in our example)to the one table.

Performing pagination

When presenting large tables of data to a user, it is often convenient to present information a page ata time. Many of Groovy’s SQL retrieval methods have extra parameters which can be used to select a particularpage of interest. The starting position and page size are specified as integers as shown in the following exampleusingrows:

Retrieving pages of data
def qry = 'SELECT * FROM Author'assert sql.rows(qry, 1, 3)*.firstname == ['Dierk', 'Paul', 'Guillaume']assert sql.rows(qry, 4, 3)*.firstname == ['Hamlet', 'Cedric', 'Erik']assert sql.rows(qry, 7, 3)*.firstname == ['Jon']
Fetching metadata

JDBC metadata can be retrieved in numerous ways. Perhaps the most basic approach is to extract themetadata from any row as shown in the following example which examines the tablename, column names and column type names:

Using row metadata
sql.eachRow("SELECT * FROM Author WHERE firstname = 'Dierk'") { row ->  def md = row.getMetaData()  assert md.getTableName(1) == 'AUTHOR'  assert (1..md.columnCount).collect{ md.getColumnName(it) } == ['ID', 'FIRSTNAME', 'LASTNAME']  assert (1..md.columnCount).collect{ md.getColumnTypeName(it) } == ['INTEGER', 'VARCHAR', 'VARCHAR']}

And another slight variant to the previous example, this time also looking at the column label:

Also using row metadata
sql.eachRow("SELECT firstname AS first FROM Author WHERE firstname = 'Dierk'") { row ->  def md = row.getMetaData()  assert md.getColumnName(1) == 'FIRSTNAME'  assert md.getColumnLabel(1) == 'FIRST'}

Accessing metadata is quite common, so Groovy also provides variants to many of its methods that let yousupply a closure that will be called once with the row metadata in addition to the normal row closurewhich is called for each row. The following example illustrates the two closure variant foreachRow:

Using row and metadata closures
def metaClosure = { meta -> assert meta.getColumnName(1) == 'FIRSTNAME' }def rowClosure = { row -> assert row.FIRSTNAME == 'Dierk' }sql.eachRow("SELECT firstname FROM Author WHERE firstname = 'Dierk'", metaClosure, rowClosure)

Note that our SQL query will only return one row, so we could have equally usedfirstRow for the previous example.

Finally, JDBC also provides metadata per connection (not just for rows). You can also access such metadata from Groovy as shown in this example:

Using connection metadata
def md = sql.connection.metaDataassert md.driverName == 'HSQL Database Engine Driver'assert md.databaseProductVersion == '2.7.3'assert ['JDBCMajorVersion', 'JDBCMinorVersion'].collect{ md[it] } == [4, 2]assert md.stringFunctions.tokenize(',').contains('CONCAT')def rs = md.getTables(null, null, 'AUTH%', null)assert rs.next()assert rs.getString('TABLE_NAME').startsWith('AUTHOR')

Consult the JavaDoc for your driver to find out what metadata information is available for you to access.

Named and named-ordinal parameters

Groovy supports some additional alternative placeholder syntax variants. The GString variantsare typically preferred over these alternatives but the alternatives are useful for Java integrationpurposes and sometimes in templating scenarios where GStrings might already be in heavy use as partof a template. The named parameter variants are much like the String plus list of parameter variants butinstead of having a list of? placeholders followed by a list of parameters, you have one or moreplaceholders having the form:propName or?.propName and a single map, named arguments or adomain object as the parameter. The map or domain object should have a property namedpropNamecorresponding to each supplied placeholder.

Here is an example using the colon form:

Named parameters (colon form)
sql.execute "INSERT INTO Author (firstname, lastname) VALUES (:first, :last)", first: 'Dierk', last: 'Koenig'

And another example using the question mark form:

Named parameters (question mark form)
sql.execute "INSERT INTO Author (firstname, lastname) VALUES (?.first, ?.last)", first: 'Jon', last: 'Skeet'

If the information you need to supply is spread across multiple maps or domain objects you canuse the question mark form with an additional ordinal index as shown here:

Named-ordinal parameters
class Rockstar { String first, last }def pogo = new Rockstar(first: 'Paul', last: 'McCartney')def map = [lion: 'King']sql.execute "INSERT INTO Author (firstname, lastname) VALUES (?1.first, ?2.lion)", pogo, map
Stored procedures

The exact syntax for creating a stored procedure or function varies slightly between different databases.For the HSQLDB database we are using, we can create a stored function which returns the initials of all authors in a tableas follows:

Creating a stored function
sql.execute """  CREATE FUNCTION SELECT_AUTHOR_INITIALS()  RETURNS TABLE (firstInitial VARCHAR(1), lastInitial VARCHAR(1))  READS SQL DATA  RETURN TABLE (    SELECT LEFT(Author.firstname, 1) as firstInitial, LEFT(Author.lastname, 1) as lastInitial    FROM Author  )"""

We can use a SQLCALL statement to invoke the function using Groovy’s normal SQL retrieval methods.Here is an example usingeachRow.

Creating a stored procedure or function
def result = []sql.eachRow('CALL SELECT_AUTHOR_INITIALS()') {  result << "$it.firstInitial$it.lastInitial"}assert result == ['DK', 'JS', 'GL']

Here is the code for creating another stored function, this one taking the lastname as a parameter:

Creating a stored function with a parameter
sql.execute """  CREATE FUNCTION FULL_NAME (p_lastname VARCHAR(64))  RETURNS VARCHAR(100)  READS SQL DATA  BEGIN ATOMIC    DECLARE ans VARCHAR(100);    SELECT CONCAT(firstname, ' ', lastname) INTO ans    FROM Author WHERE lastname = p_lastname;    RETURN ans;  END"""

We can use the placeholder syntax to specify where the parameter belongs and note the special placeholder position to indicate the result:

Using a stored function with a parameter
def result = sql.firstRow("{? = call FULL_NAME(?)}", ['Koenig'])assert result[0] == 'Dierk Koenig'

Finally, here is a stored procedure with input and output parameters:

Creating a stored procedure with input and output parameters
sql.execute """  CREATE PROCEDURE CONCAT_NAME (OUT fullname VARCHAR(100),    IN first VARCHAR(50), IN last VARCHAR(50))  BEGIN ATOMIC    SET fullname = CONCAT(first, ' ', last);  END"""

To use theCONCAT_NAME stored procedure parameter, we make use of a specialcall method. Any input parameters are simply providedas parameters to the method call. For output parameters, the resulting type must be specified as shown here:

Using a stored procedure with input and output parameters
sql.call("{call CONCAT_NAME(?, ?, ?)}", [Sql.VARCHAR, 'Dierk', 'Koenig']) {  fullname -> assert fullname == 'Dierk Koenig'}
Creating a stored procedure with an input/output parameter
sql.execute """  CREATE PROCEDURE CHECK_ID_POSITIVE_IN_OUT ( INOUT p_err VARCHAR(64), IN pparam INTEGER, OUT re VARCHAR(15))  BEGIN ATOMIC    IF pparam > 0 THEN      set p_err = p_err || '_OK';      set re = 'RET_OK';    ELSE      set p_err = p_err || '_ERROR';      set re = 'RET_ERROR';    END IF;  END;"""
Using a stored procedure with an input/output parameter
def scall = "{call CHECK_ID_POSITIVE_IN_OUT(?, ?, ?)}"sql.call scall, [Sql.inout(Sql.VARCHAR("MESSAGE")), 1, Sql.VARCHAR], {  res, p_err -> assert res == 'MESSAGE_OK' && p_err == 'RET_OK'}

3.9.5. Using DataSets

Groovy provides agroovy.sql.DataSet class which enhances thegroovy.sql.Sql classwith what can be thought of as miniORM functionality.Databases are accessed and queried using POGO fields and operators rather than JDBC-level API calls and RDBMS column names.

So, instead of a query like:

def qry = """SELECT * FROM Author  WHERE (firstname > ?)  AND (lastname < ?)  ORDER BY lastname DESC"""def params = ['Dierk', 'Pragt']def result = sql.rows(qry, params)assert result*.firstname == ['Eric', 'Guillaume', 'Paul']

You can write code like this:

def authorDS = sql.dataSet('Author')def result = authorDS.findAll{ it.firstname > 'Dierk' }        .findAll{ it.lastname < 'Pragt' }        .sort{ it.lastname }        .reverse()assert result.rows()*.firstname == ['Eric', 'Guillaume', 'Paul']

Here we have a helper "domain" class:

class Author {    String firstname    String lastname}

Database access and manipulation involves creating or working withinstances of the domain class.

3.10. Querying collections in SQL-like style

Groovy’sgroovy-ginq module provides a higher-level abstraction over collections.It could perform queries against in-memory collections of objects in SQL-like style.Also, querying XML, JSON, YAML, etc. could also be supported because they can be parsed into collections.As GORM and jOOQ are powerful enough to support querying DB, we will cover collections first.

3.10.1. GINQ a.k.a. Groovy-Integrated Query

GINQ is a DSL for querying with SQL-like syntax, which consists of the following structure:

GQ, i.e. abbreviation for GINQ|__ from|   |__ <data_source_alias> in <data_source>|__ [join/innerjoin/leftjoin/rightjoin/fulljoin/crossjoin]*|   |__ <data_source_alias> in <data_source>|   |__ on <condition> ((&& | ||) <condition>)* (NOTE: `crossjoin` does not need `on` clause)|__ [where]|   |__ <condition> ((&& | ||) <condition>)*|__ [groupby]|   |__ <expression> [as <alias>] (, <expression> [as <alias>])*|   |__ [having]|       |__ <condition> ((&& | ||) <condition>)*|__ [orderby]|   |__ <expression> [in (asc|desc)] (, <expression> [in (asc|desc)])*|__ [limit]|   |__ [<offset>,] <size>|__ select    |__ <expression> [as <alias>] (, <expression> [as <alias>])*
[] means the related clause is optional,* means zero or more times, and+ means one or more times. Also, the clauses of GINQ are order sensitive,so the order of clauses should be kept as the above structure

As we could see, the simplest GINQ consists of afrom clause and aselect clause, which looks like:

from n in [0, 1, 2]select n
ONLY ONEfrom clause is required in GINQ. Also, GINQ supports multiple data sources throughfrom and the related joins.

As a DSL, GINQ should be wrapped with the following block to be executed:

GQ { /* GINQ CODE */ }

For example,

def numbers = [0, 1, 2]assert [0, 1, 2] == GQ {    from n in numbers    select n}.toList()
import java.util.stream.Collectorsdef numbers = [0, 1, 2]assert '0#1#2' == GQ {    from n in numbers    select n}.stream()    .map(e -> String.valueOf(e))    .collect(Collectors.joining('#'))

And it is strongly recommended to usedef to define the variable for the result of GINQ execution,which is aQueryable instance that is lazy.

def result = GQ {    /* GINQ CODE */}def stream = result.stream() // get the stream from GINQ resultdef list = result.toList() // get the list from GINQ result
Currently GINQ can not work well when STC is enabled.

Also, GINQ could be written in a method marked with@GQ:

@GQdef someGinqMethod() {    /* GINQ CODE */}

For example,

  • Mark theginq method as a GINQ method with@GQ annotation:

@groovy.ginq.transform.GQdef ginq(list, b, e) {    from n in list    where b < n && n < e    select n}assert [3, 4] == ginq([1, 2, 3, 4, 5, 6], 2, 5).toList()
  • Specify the result type asList:

import groovy.ginq.transform.GQ@GQ(List)def ginq(b, e) {    from n in [1, 2, 3, 4, 5, 6]    where b < n && n < e    select n}assert [3, 4] == ginq(2, 5)
GINQ supports many result types, e.g.List,Set,Collection,Iterable,Iterator,java.util.stream.Stream and array types.
  • Enable parallel querying:

import groovy.ginq.transform.GQ@GQ(parallel=true)def ginq(x) {    from n in [1, 2, 3]    where n < x    select n}assert [1] == ginq(2).toList()
GINQ Syntax
Data Source

The data source for GINQ could be specified byfrom clause, which is equivalent to SQL’sFROM.Currently GINQ supportsIterable,Stream, array and GINQ result set as its data source:

Iterable Data Source
from n in [1, 2, 3] select n
Stream Data Source
from n in [1, 2, 3].stream() select n
Array Data Source
from n in new int[] {1, 2, 3} select n
GINQ Result Set Data Source
def vt = GQ {from m in [1, 2, 3] select m}assert [1, 2, 3] == GQ {    from n in vt select n}.toList()
Projection

The column names could be renamed withas clause:

def result = GQ {    from n in [1, 2, 3]    select Math.pow(n, 2) as powerOfN}assert [[1, 1], [4, 4], [9, 9]] == result.stream().map(r -> [r[0], r.powerOfN]).toList()
The renamed column could be referenced by its new name, e.g.r.powerOfN.Also, it could be referenced by its index, e.g.r[0]
assert [[1, 1], [2, 4], [3, 9]] == GQ {    from v in (        from n in [1, 2, 3]        select n, Math.pow(n, 2) as powerOfN    )    select v.n, v.powerOfN}.toList()
select P1, P2, …​, Pn is a simplified syntax ofselect new NamedRecord(P1, P2, …​, Pn) when and only whenn >= 2.Also,NamedRecord instance will be created ifas clause is used.The values stored in theNamedRecord could be referenced by their names.

Construct new objects as column values:

@groovy.transform.EqualsAndHashCodeclass Person {    String name    Person(String name) {        this.name = name    }}def persons = [new Person('Daniel'), new Person('Paul'), new Person('Eric')]assert persons == GQ {    from n in ['Daniel', 'Paul', 'Eric']    select new Person(n)}.toList()
Distinct

distinct is equivalent to SQL’sDISTINCT

def result = GQ {    from n in [1, 2, 2, 3, 3, 3]    select distinct(n)}assert [1, 2, 3] == result.toList()
def result = GQ {    from n in [1, 2, 2, 3, 3, 3]    select distinct(n, n + 1)}assert [[1, 2], [2, 3], [3, 4]] == result.toList()
Filtering

where is equivalent to SQL’sWHERE

from n in [0, 1, 2, 3, 4, 5]where n > 0 && n <= 3select n * 2
In
from n in [0, 1, 2]where n in [1, 2]select n
from n in [0, 1, 2]where n in (    from m in [1, 2]    select m)select n
import static groovy.lang.Tuple.tupleassert [0, 1] == GQ {    from n in [0, 1, 2]    where tuple(n, n + 1) in (        from m in [1, 2]        select m - 1, m    )    select n}.toList()
Not In
from n in [0, 1, 2]where n !in [1, 2]select n
from n in [0, 1, 2]where n !in (    from m in [1, 2]    select m)select n
import static groovy.lang.Tuple.tupleassert [2] == GQ {    from n in [0, 1, 2]    where tuple(n, n + 1) !in (        from m in [1, 2]        select m - 1, m    )    select n}.toList()
Exists
from n in [1, 2, 3]where (    from m in [2, 3]    where m == n    select m).exists()select n
Not Exists
from n in [1, 2, 3]where !(    from m in [2, 3]    where m == n    select m).exists()select n
Joining

More data sources for GINQ could be specified by join clauses.

from n1 in [1, 2, 3]join n2 in [1, 3] on n1 == n2select n1, n2
join is preferred overinnerjoin andinnerhashjoin as it has better readability,and it is smart enough to choose the correct concrete join(i.e.innerjoin orinnerhashjoin) by itson clause.
from n1 in [1, 2, 3]innerjoin n2 in [1, 3] on n1 == n2select n1, n2
from n1 in [1, 2, 3]leftjoin n2 in [2, 3, 4] on n1 == n2select n1, n2
from n1 in [2, 3, 4]rightjoin n2 in [1, 2, 3] on n1 == n2select n1, n2
from n1 in [1, 2, 3]fulljoin n2 in [2, 3, 4] on n1 == n2select n1, n2
from n1 in [1, 2, 3]crossjoin n2 in [3, 4, 5]select n1, n2

hash join is especially efficient when data sources contain lots of objects

from n1 in [1, 2, 3]innerhashjoin n2 in [1, 3] on n1 == n2select n1, n2
from n1 in [1, 2, 3]lefthashjoin n2 in [2, 3, 4] on n1 == n2select n1, n2
from n1 in [2, 3, 4]righthashjoin n2 in [1, 2, 3] on n1 == n2select n1, n2
from n1 in [1, 2, 3]fullhashjoin n2 in [2, 3, 4] on n1 == n2select n1, n2
Only binary expressions(==,&&) are allowed in theon clause of hash join
Grouping

groupby is equivalent to SQL’sGROUP BY, andhaving is equivalent to SQL’sHAVING

from n in [1, 1, 3, 3, 6, 6, 6]groupby nselect n, count(n)
from n in [1, 1, 3, 3, 6, 6, 6]groupby nhaving n >= 3select n, count(n)
from n in [1, 1, 3, 3, 6, 6, 6]groupby nhaving count() < 3select n, count()

The group columns could be renamed withas clause:

from s in ['ab', 'ac', 'bd', 'acd', 'bcd', 'bef']groupby s.size() as length, s[0] as firstCharselect length, firstChar, max(s)
from s in ['ab', 'ac', 'bd', 'acd', 'bcd', 'bef']groupby s.size() as length, s[0] as firstCharhaving length == 3 && firstChar == 'b'select length, firstChar, max(s)
Aggregate Functions

GINQ provides some built-in aggregate functions:

FunctionArgument Type(s)Return TypeDescription

count()

java.lang.Long

number of rows, similar tocount(*) in SQL

count(expression)

any

java.lang.Long

number of rows for which the value ofexpression is notnull

min(expression)

java.lang.Comparable

same as argument type

minimum value of expression across all non-null values

max(expression)

java.lang.Comparable

same as argument type

maximum value of expression across all non-null values

sum(expression)

java.lang.Number

java.math.BigDecimal

sum of expression across all non-null values

avg(expression)

java.lang.Number

java.math.BigDecimal

the average (arithmetic mean) of all non-null values

list(expression)

any

java.util.List

the aggregated list of all non-null values

median(expression)

java.lang.Number

java.math.BigDecimal

value such that the number of non-null values above and below it is the same ("middle" value, not necessarily same as average or mean)

stdev(expression)

java.lang.Number

java.math.BigDecimal

the statistical standard deviation of all non-null values

stdevp(expression)

java.lang.Number

java.math.BigDecimal

the statistical standard deviation for the population for all non-null values

var(expression)

java.lang.Number

java.math.BigDecimal

the statistical variance of all non-null values

varp(expression)

java.lang.Number

java.math.BigDecimal

the statistical variance for the population for all non-null values

agg(expression)

any

any

customizes the aggregation logic inexpression and returns single value

from n in [1, 1, 3, 3, 6, 6, 6]groupby nselect n, count()
from s in ['a', 'b', 'cd', 'ef']groupby s.size() as lengthselect length, min(s)
from s in ['a', 'b', 'cd', 'ef']groupby s.size() as lengthselect length, max(s)
from n in [1, 1, 3, 3, 6, 6, 6]groupby nselect n, sum(n)
from n in [1, 1, 3, 3, 6, 6, 6]groupby nselect n, avg(n)
from n in [1, 1, 3, 3, 6, 6, 6]groupby nselect n, median(n)
assert [['A', ['APPLE', 'APRICOT']],        ['B', ['BANANA']],        ['C', ['CANTALOUPE']]] == GQL {    from fruit in ['Apple', 'Apricot', 'Banana', 'Cantaloupe']    groupby fruit[0] as firstChar    select firstChar, list(fruit.toUpperCase()) as fruit_list}
def persons = [new Person('Linda', 100, 'Female'),               new Person('Daniel', 135, 'Male'),               new Person('David', 122, 'Male')]assert [['Male', ['Daniel', 'David']], ['Female', ['Linda']]] == GQL {    from p in persons    groupby p.gender    select p.gender, list(p.name)}
from n in [1, 1, 3, 3, 6, 6, 6]groupby nselect n, agg(_g.stream().map(r -> r.n).reduce(BigDecimal.ZERO, BigDecimal::add))
_g is an implicit variable foragg aggregate function,it represents the groupedQueryable object and its record(e.g.r) could reference the data source by alias(e.g.n)
from fruit in ['Apple', 'Apricot', 'Banana', 'Cantaloupe']groupby fruit.substring(0, 1) as firstCharselect firstChar, agg(_g.stream().map(r -> r.fruit).toList()) as fruit_list

Also, we could apply the aggregate functions for the whole GINQ result, i.e. nogroupby clause is needed:

assert [3] == GQ {    from n in [1, 2, 3]    select max(n)}.toList()
assert [[1, 3, 2, 2, 6, 3, 3, 6]] == GQ {    from n in [1, 2, 3]    select min(n), max(n), avg(n), median(n), sum(n), count(n), count(),            agg(_g.stream().map(r -> r.n).reduce(BigDecimal.ZERO, BigDecimal::add))}.toList()
assert [0.816496580927726] == GQ {    from n in [1, 2, 3]    select stdev(n)}.toList()
assert [1] == GQ {    from n in [1, 2, 3]    select stdevp(n)}.toList()
assert [0.6666666666666667] == GQ {    from n in [1, 2, 3]    select var(n)}.toList()
assert [1] == GQ {    from n in [1, 2, 3]    select varp(n)}.toList()
Sorting

orderby is equivalent to SQL’sORDER BY

from n in [1, 5, 2, 6]orderby nselect n
in asc is optional when sortingin ascending order
from n in [1, 5, 2, 6]orderby n in ascselect n
from n in [1, 5, 2, 6]orderby n in descselect n
from s in ['a', 'b', 'ef', 'cd']orderby s.length() in desc, s in ascselect s
from s in ['a', 'b', 'ef', 'cd']orderby s.length() in desc, sselect s
from n in [1, null, 5, null, 2, 6]orderby n in asc(nullslast)select n
nullslast is equivalent to SQL’sNULLS LAST and applied by default.nullsfirst is equivalent to SQL’sNULLS FIRST.
from n in [1, null, 5, null, 2, 6]orderby n in asc(nullsfirst)select n
from n in [1, null, 5, null, 2, 6]orderby n in desc(nullslast)select n
from n in [1, null, 5, null, 2, 6]orderby n in desc(nullsfirst)select n
Pagination

limit is similar to thelimit clause of MySQL, which could specify theoffset(first argument) andsize(second argument) for paginating,or just specify the only one argument assize

from n in [1, 2, 3, 4, 5]limit 3select n
from n in [1, 2, 3, 4, 5]limit 1, 3select n
Nested GINQ
Nested GINQ infrom clause
from v in (    from n in [1, 2, 3]    select n)select v
Nested GINQ inwhere clause
from n in [0, 1, 2]where n in (    from m in [1, 2]    select m)select n
from n in [0, 1, 2]where (    from m in [1, 2]    where m == n    select m).exists()select n
Nested GINQ inselect clause
assert [null, 2, 3] == GQ {    from n in [1, 2, 3]    select (        from m in [2, 3, 4]        where m == n        limit 1        select m    )}.toList()
It’s recommended to uselimit 1 to restrict the count of sub-query resultbecauseTooManyValuesException will be thrown if more than one values returned

We could useas clause to name the sub-query result

assert [[1, null], [2, 2], [3, 3]] == GQ {    from n in [1, 2, 3]    select n, (        from m in [2, 3, 4]        where m == n        select m    ) as sqr}.toList()
Window Functions

Window can be defined bypartitionby,orderby,rows andrange:

over(    [partitionby <expression> (, <expression>)*]    [orderby <expression> (, <expression>)*       [rows <lower>, <upper> | range <lower>, <upper>]])
  • 0 used as bound ofrows andrange clause is equivalent to SQL’sCURRENT ROW, and negative meansPRECEDING, positive meansFOLLOWING

  • null used as the lower bound ofrows andrange clause is equivalent to SQL’sUNBOUNDED PRECEDING

  • null used as the upper bound ofrows andrange clause is equivalent to SQL’sUNBOUNDED FOLLOWING

Also, GINQ provides some built-in window functions:

FunctionArgument Type(s)Return TypeDescription

rowNumber()

java.lang.Long

number of the current row within its partition, counting from0

rank()

java.lang.Long

rank of the current row with gaps

denseRank()

java.lang.Long

rank of the current row without gaps

percentRank()

java.math.BigDecimal

relative rank of the current row: (rank - 1) / (total rows - 1)

cumeDist()

java.math.BigDecimal

relative rank of the current row: (number of rows preceding or peer with current row) / (total rows)

ntile(expression)

java.lang.Long

java.lang.Long

bucket index ranging from0 toexpression - 1, dividing the partition as equally as possible

lead(expression [,offset [,default]])

any [, java.lang.Long [, same asexpression type]]

same asexpression type

returnsexpression evaluated at the row that isoffset rows after the current row within the partition; if there is no such row, instead returndefault (which must be of the same type asexpression). Bothoffset anddefault are evaluated with respect to the current row. If omitted,offset defaults to1 anddefault tonull

lag(expression [,offset [,default]])

any [, java.lang.Long [, same asexpression type]]

same asexpression type

returnsexpression evaluated at the row that isoffset rows before the current row within the partition; if there is no such row, instead returndefault (which must be of the same type asexpression). Bothoffset anddefault are evaluated with respect to the current row. If omitted,offset defaults to1 anddefault tonull

firstValue(expression)

any

same type asexpression

returnsexpression evaluated at the row that is the first row of the window frame

lastValue(expression)

any

same type asexpression

returnsexpression evaluated at the row that is the last row of the window frame

nthValue(expression,n)

any, java.lang.Long

same type asexpression

returnsexpression evaluated at the row that is thenth row of the window frame

count()

java.lang.Long

number of rows, similar tocount(*) in SQL

count(expression)

any

java.lang.Long

number of rows for which the value ofexpression is notnull

min(expression)

java.lang.Comparable

same as argument type

minimum value of expression across all non-null values

max(expression)

java.lang.Comparable

same as argument type

maximum value of expression across all non-null values

sum(expression)

java.lang.Number

java.math.BigDecimal

sum of expression across all non-null values

avg(expression)

java.lang.Number

java.math.BigDecimal

the average (arithmetic mean) of all non-null values

median(expression)

java.lang.Number

java.math.BigDecimal

value such that the number of non-null values above and below it is the same ("middle" value, not necessarily same as average or mean)

stdev(expression)

java.lang.Number

java.math.BigDecimal

the statistical standard deviation of all non-null values

stdevp(expression)

java.lang.Number

java.math.BigDecimal

the statistical standard deviation for the population for all non-null values

var(expression)

java.lang.Number

java.math.BigDecimal

the statistical variance of all non-null values

varp(expression)

java.lang.Number

java.math.BigDecimal

the statistical variance for the population for all non-null values

agg(expression)

any

any

INCUBATING: customizes the aggregation logic inexpression and returns single value

rowNumber
assert [[2, 1, 1, 1], [1, 0, 0, 2], [null, 3, 3, 3], [3, 2, 2, 0]] == GQ {    from n in [2, 1, null, 3]    select n, (rowNumber() over(orderby n)),              (rowNumber() over(orderby n in asc)),              (rowNumber() over(orderby n in desc))}.toList()
assert [[1, 0, 1, 2, 3], [2, 1, 2, 1, 2], [null, 3, 0, 3, 0], [3, 2, 3, 0, 1]] == GQ {    from n in [1, 2, null, 3]    select n, (rowNumber() over(orderby n in asc(nullslast))),              (rowNumber() over(orderby n in asc(nullsfirst))),              (rowNumber() over(orderby n in desc(nullslast))),              (rowNumber() over(orderby n in desc(nullsfirst)))}.toList()
The parentheses around the window function is required.
rank,denseRank,percentRank,cumeDist andntile
assert [['a', 1, 1], ['b', 2, 2], ['b', 2, 2],        ['c', 4, 3], ['c', 4, 3], ['d', 6, 4],        ['e', 7, 5]] == GQ {    from s in ['a', 'b', 'b', 'c', 'c', 'd', 'e']    select s,        (rank() over(orderby s)),        (denseRank() over(orderby s))}.toList()
assert [[60, 0, 0.4], [60, 0, 0.4], [80, 0.5, 0.8], [80, 0.5, 0.8], [100, 1, 1]] == GQ {    from n in [60, 60, 80, 80, 100]    select n,        (percentRank() over(orderby n)),        (cumeDist() over(orderby n))}.toList()
assert [[1, 0], [2, 0], [3, 0],        [4, 1], [5, 1],        [6, 2], [7, 2],[8, 2],        [9, 3], [10, 3]] == GQ {    from n in 1..10    select n, (ntile(4) over(orderby n))}.toList()
lead andlag
assert [[2, 3], [1, 2], [3, null]] == GQ {    from n in [2, 1, 3]    select n, (lead(n) over(orderby n))}.toList()
assert [[2, 3], [1, 2], [3, null]] == GQ {    from n in [2, 1, 3]    select n, (lead(n) over(orderby n in asc))}.toList()
assert [['a', 'bc'], ['ab', null], ['b', 'a'], ['bc', 'ab']] == GQ {    from s in ['a', 'ab', 'b', 'bc']    select s, (lead(s) over(orderby s.length(), s in desc))}.toList()
assert [['a', null], ['ab', null], ['b', 'a'], ['bc', 'ab']] == GQ {    from s in ['a', 'ab', 'b', 'bc']    select s, (lead(s) over(partitionby s.length() orderby s.length(), s in desc))}.toList()
assert [[2, 1], [1, null], [3, 2]] == GQ {    from n in [2, 1, 3]    select n, (lag(n) over(orderby n))}.toList()
assert [[2, 3], [1, 2], [3, null]] == GQ {    from n in [2, 1, 3]    select n, (lag(n) over(orderby n in desc))}.toList()
assert [['a', null], ['b', 'a'], ['aa', null], ['bb', 'aa']] == GQ {    from s in ['a', 'b', 'aa', 'bb']    select s, (lag(s) over(partitionby s.length() orderby s))}.toList()
assert [[2, 3, 1], [1, 2, null], [3, null, 2]] == GQ {    from n in [2, 1, 3]    select n, (lead(n) over(orderby n)), (lag(n) over(orderby n))}.toList()

The offset can be specified other than the default offset1:

assert [[2, null, null], [1, 3, null], [3, null, 1]] == GQ {    from n in [2, 1, 3]    select n, (lead(n, 2) over(orderby n)), (lag(n, 2) over(orderby n))}.toList()

The default value can be returned when the index specified by offset is out of window, e.g.'NONE':

assert [[2, 'NONE', 'NONE'], [1, 3, 'NONE'], [3, 'NONE', 1]] == GQ {    from n in [2, 1, 3]    select n, (lead(n, 2, 'NONE') over(orderby n)), (lag(n, 2, 'NONE') over(orderby n))}.toList()
firstValue,lastValue andnthValue
assert [[2, 1], [1, 1], [3, 2]] == GQ {    from n in [2, 1, 3]    select n, (firstValue(n) over(orderby n rows -1, 1))}.toList()
assert [[2, 3], [1, 2], [3, 3]] == GQ {    from n in [2, 1, 3]    select n, (lastValue(n) over(orderby n rows -1, 1))}.toList()
assert [[2, 2], [1, 1], [3, 3]] == GQ {    from n in [2, 1, 3]    select n, (firstValue(n) over(orderby n rows 0, 1))}.toList()
assert [[2, 1], [1, null], [3, 1]] == GQ {    from n in [2, 1, 3]    select n, (firstValue(n) over(orderby n rows -2, -1))}.toList()
assert [[2, 1], [1, null], [3, 2]] == GQ {    from n in [2, 1, 3]    select n, (lastValue(n) over(orderby n rows -2, -1))}.toList()
assert [[2, 3], [1, 3], [3, null]] == GQ {    from n in [2, 1, 3]    select n, (lastValue(n) over(orderby n rows 1, 2))}.toList()
assert [[2, 3], [1, 2], [3, null]] == GQ {    from n in [2, 1, 3]    select n, (firstValue(n) over(orderby n rows 1, 2))}.toList()
assert [[2, 2], [1, 1], [3, 3]] == GQ {    from n in [2, 1, 3]    select n, (lastValue(n) over(orderby n rows -1, 0))}.toList()
assert [[2, 1], [1, 1], [3, 1]] == GQ {    from n in [2, 1, 3]    select n, (firstValue(n) over(orderby n rows null, 1))}.toList()
assert [[2, 3], [1, 3], [3, 3]] == GQ {    from n in [2, 1, 3]    select n, (lastValue(n) over(orderby n rows -1, null))}.toList()
assert [['a', 'a', 'b'], ['aa', 'aa', 'bb'], ['b', 'a', 'b'], ['bb', 'aa', 'bb']] == GQ {    from s in ['a', 'aa', 'b', 'bb']    select s, (firstValue(s) over(partitionby s.length() orderby s)),            (lastValue(s) over(partitionby s.length() orderby s))}.toList()
assert [[1, 1, 2, 3, null], [2, 1, 2, 3, null], [3, 1, 2, 3, null]] == GQ {    from n in 1..3    select n, (nthValue(n, 0) over(orderby n)),              (nthValue(n, 1) over(orderby n)),              (nthValue(n, 2) over(orderby n)),              (nthValue(n, 3) over(orderby n))}.toList()
min,max,count,sum,avg,median,stdev,stdevp,var ,varp andagg
assert [['a', 'a', 'b'], ['b', 'a', 'b'], ['aa', 'aa', 'bb'], ['bb', 'aa', 'bb']] == GQ {    from s in ['a', 'b', 'aa', 'bb']    select s, (min(s) over(partitionby s.length())), (max(s) over(partitionby s.length()))}.toList()
assert [[1, 2, 2, 2, 1, 1], [1, 2, 2, 2, 1, 1],        [2, 2, 2, 4, 2, 2], [2, 2, 2, 4, 2, 2],        [3, 2, 2, 6, 3, 3], [3, 2, 2, 6, 3, 3]] == GQ {    from n in [1, 1, 2, 2, 3, 3]    select n, (count() over(partitionby n)),              (count(n) over(partitionby n)),              (sum(n) over(partitionby n)),              (avg(n) over(partitionby n)),              (median(n) over(partitionby n))}.toList()
assert [[2, 6, 3, 1, 3, 4], [1, 6, 3, 1, 3, 4],        [3, 6, 3, 1, 3, 4], [null, 6, 3, 1, 3, 4]] == GQ {    from n in [2, 1, 3, null]    select n, (sum(n) over()),              (max(n) over()),              (min(n) over()),              (count(n) over()),              (count() over())}.toList()
assert [[1, 1, 1], [2, 2, 3], [5, 2, 10], [5, 2, 10]] == GQ {    from n in [1, 2, 5, 5]    select n, (count() over(orderby n range -2, 0)),              (sum(n) over(orderby n range -2, 0))}.toList()
assert [[1, 2, 3], [2, 1, 2], [5, 2, 10], [5, 2, 10]] == GQ {    from n in [1, 2, 5, 5]    select n, (count() over(orderby n range 0, 1)),              (sum(n) over(orderby n range 0, 1))}.toList()
assert [[1, 2, 3], [2, 2, 3], [5, 2, 10], [5, 2, 10]] == GQ {    from n in [1, 2, 5, 5]    select n, (count() over(orderby n range -1, 1)),              (sum(n) over(orderby n range -1, 1))}.toList()
assert [[1, 1, 2], [2, 0, 0], [5, 0, 0], [5, 0, 0]] == GQ {    from n in [1, 2, 5, 5]    select n, (count() over(orderby n in desc range 1, 2)),              (sum(n) over(orderby n in desc range 1, 2))}.toList()
assert [[1, 0, 0], [2, 1, 1], [5, 0, 0], [5, 0, 0]] == GQ {    from n in [1, 2, 5, 5]    select n, (count() over(orderby n in desc range -2, -1)),              (sum(n) over(orderby n in desc range -2, -1))}.toList()
assert [[1, 3, 12], [2, 2, 10], [5, 0, 0], [5, 0, 0]] == GQ {    from n in [1, 2, 5, 5]    select n, (count() over(orderby n range 1, null)),              (sum(n) over(orderby n range 1, null))}.toList()
assert [[1, 2, 3], [2, 2, 3], [5, 4, 13], [5, 4, 13]] == GQ {    from n in [1, 2, 5, 5]    select n, (count() over(orderby n range null, 1)),              (sum(n) over(orderby n range null, 1))}.toList()
assert [[1, 0.816496580927726],        [2, 0.816496580927726],        [3, 0.816496580927726]] == GQ {    from n in [1, 2, 3]    select n, (stdev(n) over())}.toList()
assert [[1, 1], [2, 1], [3, 1]] == GQ {    from n in [1, 2, 3]    select n, (stdevp(n) over())}.toList()
assert [[1, 0.6666666666666667],        [2, 0.6666666666666667],        [3, 0.6666666666666667]] == GQ {    from n in [1, 2, 3]    select n, (var(n) over())}.toList()
assert [[1, 1], [2, 1], [3, 1]] == GQ {    from n in [1, 2, 3]    select n, (varp(n) over())}.toList()
assert [[1, 4], [2, 2], [3, 4]] == GQ {    from n in [1, 2, 3]    select n,           (agg(_g.stream().map(r -> r.n).reduce(BigDecimal.ZERO, BigDecimal::add)) over(partitionby n % 2))}.toList()
GINQ Tips
Row Number

_rn is the implicit variable representing row number for each record in the result set. It starts with0

from n in [1, 2, 3]select _rn, n
List Comprehension

List comprehension is an elegant way to define and create lists based on existing lists:

assert [4, 16, 36, 64, 100] == GQ {from n in 1..<11 where n % 2 == 0 select n ** 2}.toList()
assert [4, 16, 36, 64, 100] == GQ {from n in 1..<11 where n % 2 == 0 select n ** 2} as List
assert [4, 16, 36, 64, 100] == GQL {from n in 1..<11 where n % 2 == 0 select n ** 2}
GQL {…​} is the abbreviation ofGQ {…​}.toList()

GINQ could be used as list comprehension in the loops directly:

def result = []for (def x : GQ {from n in 1..<11 where n % 2 == 0 select n ** 2}) {    result << x}assert [4, 16, 36, 64, 100] == result
Query & Update

This is likeupdate statement in SQL

import groovy.transform.*@TupleConstructor@EqualsAndHashCode@ToStringclass Person {    String name    String nickname}def linda = new Person('Linda', null)def david = new Person('David', null)def persons = [new Person('Daniel', 'ShanFengXiaoZi'), linda, david]def result = GQ {    from p in persons    where p.nickname == null    select p}.stream()    .peek(p -> { p.nickname = 'Unknown' }) // update `nickname`    .toList()def expected = [new Person('Linda', 'Unknown'), new Person('David', 'Unknown')]assert expected == resultassert ['Unknown', 'Unknown'] == [linda, david]*.nickname // ensure the original objects are updated
Alternative forwith clause

GINQ does not supportwith clause for now, but we could define a temporary variable to workaround:

def v = GQ { from n in [1, 2, 3] where n < 3 select n }def result = GQ {    from n in v    where n > 1    select n}assert [2] == result.toList()
Alternative forcase-when

case-when of SQL could be replaced with switch expression:

assert ['a', 'b', 'c', 'c'] == GQ {    from n in [1, 2, 3, 4]    select switch (n) {        case 1 -> 'a'        case 2 -> 'b'        default -> 'c'    }}.toList()
Query JSON
import groovy.json.JsonSlurperdef json = new JsonSlurper().parseText('''    {        "fruits": [            {"name": "Orange", "price": 11},            {"name": "Apple", "price": 6},            {"name": "Banana", "price": 4},            {"name": "Mongo", "price": 29},            {"name": "Durian", "price": 32}        ]    }''')def expected = [['Mongo', 29], ['Orange', 11], ['Apple', 6], ['Banana', 4]]assert expected == GQ {    from f in json.fruits    where f.price < 32    orderby f.price in desc    select f.name, f.price}.toList()
Parallel Querying

Parallel querying is especially efficient when querying big data sources. It is disabled by default, but we could enable it by hand:

assert [[1, 1], [2, 2], [3, 3]] == GQ(parallel: true) {    from n1 in 1..1000    join n2 in 1..10000 on n2 == n1    where n1 <= 3 && n2 <= 5    select n1, n2}.toList()

As parallel querying will use a shared thread pool, the following code can release resources after all GINQ statements execution are completed, and it will wait util all tasks of threads are completed.

GQ {    shutdown}
Onceshutdown is issued, parallel querying can not work anymore.

The following code is equivalent to the above code, in other words,immediate is optional:

GQ {    shutdown immediate}

Shutdown without waiting tasks to complete:

GQ {    shutdown abort}
Customize GINQ

For advanced users, you could customize GINQ behaviour by specifying your own target code generator.For example, we could specify the qualified class nameorg.apache.groovy.ginq.provider.collection.GinqAstWalker as the target code generator to generate GINQ method calls for querying collections,which is the default behaviour of GINQ:

assert [0, 1, 2] == GQ(astWalker: 'org.apache.groovy.ginq.provider.collection.GinqAstWalker') {    from n in [0, 1, 2]    select n}.toList()
Optimize GINQ

GINQ optimizer is enabled by default for better performance. It will transform the GINQ AST to achieve better execution plan.We could disable it by hand:

assert [[2, 2]] == GQ(optimize: false) {    from n1 in [1, 2, 3]    join n2 in [1, 2, 3] on n1 == n2    where n1 > 1 &&  n2 < 3    select n1, n2}.toList()
GINQ Examples
Generate Multiplication Table
from v in (    from a in 1..9    join b in 1..9 on a <= b    select a as f, b as s, "$a * $b = ${a * b}".toString() as r)groupby v.sselect max(v.f == 1 ? v.r : '') as v1,       max(v.f == 2 ? v.r : '') as v2,       max(v.f == 3 ? v.r : '') as v3,       max(v.f == 4 ? v.r : '') as v4,       max(v.f == 5 ? v.r : '') as v5,       max(v.f == 6 ? v.r : '') as v6,       max(v.f == 7 ? v.r : '') as v7,       max(v.f == 8 ? v.r : '') as v8,       max(v.f == 9 ? v.r : '') as v9
More examples

link: the latestGINQ examples

Some examples in the above link require the latest SNAPSHOT version of Groovy to run.

3.11. Processing XML

3.11.1. Parsing XML

XmlParser and XmlSlurper

The most commonly used approach for parsing XML with Groovy is to useone of:

  • groovy.xml.XmlParser

  • groovy.xml.XmlSlurper

Both have the same approach to parse an XML. Both come with a bunch ofoverloaded parse methods plus some special methods such asparseText,parseFile and others. For the next example we will use theparseTextmethod. It parses an XMLString and recursively converts it to a listor map of objects.

XmlSlurper
def text = '''    <list>        <technology>            <name>Groovy</name>        </technology>    </list>'''def list = new XmlSlurper().parseText(text)(1)assert list instanceof groovy.xml.slurpersupport.GPathResult(2)assert list.technology.name == 'Groovy'(3)
1Parsing the XML an returning the root node as a GPathResult
2Checking we’re using a GPathResult
3Traversing the tree in a GPath style
XmlParser
def text = '''    <list>        <technology>            <name>Groovy</name>        </technology>    </list>'''def list = new XmlParser().parseText(text)(1)assert list instanceof groovy.util.Node(2)assert list.technology.name.text() == 'Groovy'(3)
1Parsing the XML an returning the root node as a Node
2Checking we’re using a Node
3Traversing the tree in a GPath style

Let’s see thesimilarities betweenXMLParser andXMLSlurper first:

  • Both are based onSAX so they both are low memory footprint

  • Both can update/transform the XML

But they have keydifferences:

  • XmlSlurper evaluates the structure lazily. So if you update the xmlyou’ll have to evaluate the whole tree again.

  • XmlSlurper returnsGPathResult instances when parsing XML

  • XmlParser returnsNode objects when parsing XML

When to use one or the another?

There is a discussion atStackOverflow. Theconclusions written here are based partially on this entry.
  • If you want to transform an existing document to another thenXmlSlurper will be the choice

  • If you want to update and read at the same time thenXmlParser isthe choice.

The rationale behind this is that every time you create a node withXmlSlurper it won’t be available until you parse the document againwith anotherXmlSlurper instance. Need to read just a few nodesXmlSlurper is for you ".

  • If you just have to read a few nodesXmlSlurper should be yourchoice, since it will not have to create a complete structure inmemory"

In general both classes perform similar way. Even the way of usingGPath expressions with them are the same (both usebreadthFirst() anddepthFirst() expressions). So I guess it depends on the write/readfrequency.

DOMCategory

There is another way of parsing XML documents with Groovy with theuse ofgroovy.xml.dom.DOMCategory which is a category class whichadds GPath style operations to Java’s DOM classes.

Java has in-built support for DOM processing of XML using classesrepresenting the various parts of XML documents, e.g.Document,Element,NodeList,Attr etc. For more information about these classes,refer to the respective JavaDocs.

Having an XML like the following:

static def CAR_RECORDS = '''<records>  <car name='HSV Maloo' make='Holden' year='2006'>    <country>Australia</country>    <record type='speed'>Production Pickup Truck with speed of 271kph</record>  </car>  <car name='P50' make='Peel' year='1962'>    <country>Isle of Man</country>    <record type='size'>Smallest Street-Legal Car at 99cm wide and 59 kg in weight</record>  </car>  <car name='Royale' make='Bugatti' year='1931'>    <country>France</country>    <record type='price'>Most Valuable Car at $15 million</record>  </car></records>'''

You can parse it usinggroovy.xml.DOMBuilder andgroovy.xml.dom.DOMCategory.

def reader = new StringReader(CAR_RECORDS)def doc = DOMBuilder.parse(reader)(1)def records = doc.documentElementuse(DOMCategory) {(2)    assert records.car.size() == 3}
1Parsing the XML
2CreatingDOMCategory scope to be able to use helper method calls

3.11.2. GPath

The most common way of querying XML in Groovy is usingGPath:

GPath is a path expression language integrated into Groovy whichallows parts of nested structured data to be identified. In thissense, it has similar aims and scope as XPath does for XML. The twomain places where you use GPath expressions is when dealing withnested POJOs or when dealing with XML

It is similar toXPathexpressions and you can use it not only with XML but also with POJOclasses. As an example, you can specify a path to an object or elementof interest:

  • a.b.c → for XML, yields all the<c> elements inside<b> inside<a>

  • a.b.c → all POJOs, yields the<c> properties for all the<b>properties of<a> (sort of like a.getB().getC() in JavaBeans)

For XML, you can also specify attributes, e.g.:

  • a["@href"] → the href attribute of all the a elements

  • a.'@href' → an alternative way of expressing this

  • a.@href → an alternative way of expressing this when using XmlSlurper

Let’s illustrate this with an example:

static final String books = '''    <response version-api="2.0">        <value>            <books>                <book available="20">                    <title>Don Quixote</title>                    <author>Miguel de Cervantes</author>                </book>                <book available="14">                    <title>Catcher in the Rye</title>                   <author>JD Salinger</author>               </book>               <book available="13">                   <title>Alice in Wonderland</title>                   <author>Lewis Carroll</author>               </book>               <book available="5">                   <title>Don Quixote</title>                   <author>Miguel de Cervantes</author>               </book>           </books>       </value>    </response>'''
Simply traversing the tree

First thing we could do is to get a value using POJO’s notation. Let’sget the first book’s author’s name

Getting node value
def response = new XmlSlurper().parseText(books)def authorResult = response.value.books.book[0].authorassert authorResult.text() == 'Miguel de Cervantes'

First we parse the document withXmlSlurper and then we have toconsider the returning value as the root of the XML document, so inthis case is "response".

That’s why we start traversing the document from response and thenvalue.books.book[0].author. Note that inXPath the node arrays startsin [1] instead of [0], but becauseGPath is Java-based it begins atindex 0.

In the end we’ll have the instance of theauthor node and because wewanted the text inside that node we should be calling thetext()method. Theauthor node is an instance ofGPathResult type andtext() a method giving us the content of that node as a String.

When usingGPath with an XML parsed withXmlSlurper we’ll have as aresult aGPathResult object.GPathResult has many other convenientmethods to convert the text inside a node to any other type such as:

  • toInteger()

  • toFloat()

  • toBigInteger()

  • …​

All these methods try to convert aString to the appropriate type.

If we were using an XML parsed withXmlParser we could be dealing withinstances of typeNode. But still all the actions applied toGPathResult in these examples could be applied to a Node aswell. Creators of both parsers took into accountGPath compatibility.

Next step is to get some values from a given node’s attribute. In the following samplewe want to get the first book’s author’s id. We’ll be using two different approaches. Let’s see the code first:

Getting an attribute’s value
def response = new XmlSlurper().parseText(books)def book = response.value.books.book[0](1)def bookAuthorId1 = book.@id(2)def bookAuthorId2 = book['@id'](3)assert bookAuthorId1 == '1'(4)assert bookAuthorId1.toInteger() == 1(5)assert bookAuthorId1 == bookAuthorId2
1Getting the first book node
2Getting the book’s id attribute@id
3Getting the book’s id attribute withmap notation['@id']
4Getting the value as a String
5Getting the value of the attribute as anInteger

As you can see there are two types of notations to get attributes,the

  • direct notation with@nameoftheattribute

  • map notation using['@nameoftheattribute']

Both of them are equally valid.

Flexible navigation with children (*), depthFirst (**) and breadthFirst

If you ever have used XPath, you may have used expressions like:

  • /following-sibling::othernode : Look for a node "othernode" in the same level

  • // : Look everywhere

More or less we have their counterparts in GPath with the shortcuts* (akachildren()) and** (akadepthFirst()).

The first example shows a simple use of*, which only iterates over the direct children of the node.

Using *
def response = new XmlSlurper().parseText(books)// .'*' could be replaced by .children()def catcherInTheRye = response.value.books.'*'.find { node ->    // node.@id == 2 could be expressed as node['@id'] == 2    node.name() == 'book' && node.@id == '2'}assert catcherInTheRye.title.text() == 'Catcher in the Rye'

This test searches for any child nodes of the "books" node matching the givencondition. In a bit more detail, the expression says:Look for any node witha tag name equal to 'book' having an id with a value of '2' directly underthe 'books' node.

This operation roughly corresponds to thebreadthFirst() method, except thatit only stops atone level instead of continuing to the inner levels.

What if we would like to look for a given valuewithout having to know exactly where it is. Let’s say that theonly thing we know is the id of the author "Lewis Carroll" . How arewe going to be able to find that book? Using** is the solution:

Using **
def response = new XmlSlurper().parseText(books)// .'**' could be replaced by .depthFirst()def bookId = response.'**'.find { book ->    book.author.text() == 'Lewis Carroll'}.@idassert bookId == 3

** is the same as looking for somethingeverywhere in thetree from this point down. In this case, we’ve used the methodfind(Closure cl) to find just the first occurrence.

What if we want to collect all book’s titles? That’s easy, just usefindAll:

def response = new XmlSlurper().parseText(books)def titles = response.'**'.findAll { node -> node.name() == 'title' }*.text()assert titles.size() == 4

In the last two examples,** is used as a shortcut for thedepthFirst()method. It goes as far down the tree as it can while navigating down thetree from a given node. ThebreadthFirst() method finishes off all nodeson a given level before traversing down to the next level.

The following example shows the difference between these two methods:

depthFirst() vs .breadthFirst
def response = new XmlSlurper().parseText(books)def nodeName = { node -> node.name() }def withId2or3 = { node -> node.@id in [2, 3] }assert ['book', 'author', 'book', 'author'] ==        response.value.books.depthFirst().findAll(withId2or3).collect(nodeName)assert ['book', 'book', 'author', 'author'] ==        response.value.books.breadthFirst().findAll(withId2or3).collect(nodeName)

In this example, we search for any nodes with an id attribute with value 2 or 3.There are bothbook andauthor nodes that match that criteria. The differenttraversal orders will find the same nodes in each case but in different orderscorresponding to how the tree was traversed.

It is worth mentioning again that there are some useful methodsconverting a node’s value to an integer, float, etc. Those methodscould be convenient when doing comparisons like this:

helpers
def response = new XmlSlurper().parseText(books)def titles = response.value.books.book.findAll { book ->    /* You can use toInteger() over the GPathResult object */    book.@id.toInteger() > 2}*.titleassert titles.size() == 2

In this case the number 2 has been hardcoded but imagine that valuecould have come from any other source (database…​ etc.).

3.11.3. Creating XML

The most commonly used approach for creating XML with Groovy is to usea builder, i.e. one of:

  • groovy.xml.MarkupBuilder

  • groovy.xml.StreamingMarkupBuilder

MarkupBuilder

Here is an example of using Groovy’s MarkupBuilder to create a new XML file:

Creating Xml with MarkupBuilder
def writer = new StringWriter()def xml = new MarkupBuilder(writer)(1)xml.records() {(2)    car(name: 'HSV Maloo', make: 'Holden', year: 2006) {        country('Australia')        record(type: 'speed', 'Production Pickup Truck with speed of 271kph')    }    car(name: 'Royale', make: 'Bugatti', year: 1931) {        country('France')        record(type: 'price', 'Most Valuable Car at $15 million')    }}def records = new XmlSlurper().parseText(writer.toString())(3)assert records.car.first().name.text() == 'HSV Maloo'assert records.car.last().name.text() == 'Royale'
1Create an instance ofMarkupBuilder
2Start creating the XML tree
3Create an instance ofXmlSlurper to traverse and test thegenerated XML

Let’s take a look a little bit closer:

Creating XML elements
def xmlString = "<movie>the godfather</movie>"(1)def xmlWriter = new StringWriter()(2)def xmlMarkup = new MarkupBuilder(xmlWriter)xmlMarkup.movie("the godfather")(3)assert xmlString == xmlWriter.toString()(4)
1We’re creating a reference string to compare against
2ThexmlWriter instance is used byMarkupBuilder to convert thexml representation to a String instance eventually
3ThexmlMarkup.movie(…​) call will create an XML node with a tagcalledmovie and with contentthe godfather.
Creating XML elements with attributes
def xmlString = "<movie id='2'>the godfather</movie>"def xmlWriter = new StringWriter()def xmlMarkup = new MarkupBuilder(xmlWriter)xmlMarkup.movie(id: "2", "the godfather")(1)assert xmlString == xmlWriter.toString()
1This time in order to create both attributes and node content youcan create as many map entries as you like and finally add a valueto set the node’s content
The value could be anyObject, the value will be serialized to itsString representation.
Creating XML nested elements
def xmlWriter = new StringWriter()def xmlMarkup = new MarkupBuilder(xmlWriter)xmlMarkup.movie(id: 2) {(1)    name("the godfather")}def movie = new XmlSlurper().parseText(xmlWriter.toString())assert movie.@id == 2assert movie.name.text() == 'the godfather'
1A closure represents the children elements of a given node. Noticethis time instead of using a String for the attribute we’re using anumber.

Sometimes you may want to use a specific namespace in your xml documents:

Namespace aware
def xmlWriter = new StringWriter()def xmlMarkup = new MarkupBuilder(xmlWriter)xmlMarkup        .'x:movies'('xmlns:x': 'http://www.groovy-lang.org') {(1)    'x:movie'(id: 1, 'the godfather')    'x:movie'(id: 2, 'ronin')}def movies =        new XmlSlurper()(2)                .parseText(xmlWriter.toString())                .declareNamespace(x: 'http://www.groovy-lang.org')assert movies.'x:movie'.last().@id == 2assert movies.'x:movie'.last().text() == 'ronin'
1Creating a node with a given namespacexmlns:x
2Creating aXmlSlurper registering the namespace to be able totest the XML we just created

What about having some more meaningful example. We may want togenerate more elements, to have some logic when creating our XML:

Mix code
def xmlWriter = new StringWriter()def xmlMarkup = new MarkupBuilder(xmlWriter)xmlMarkup        .'x:movies'('xmlns:x': 'http://www.groovy-lang.org') {    (1..3).each { n ->(1)        'x:movie'(id: n, "the godfather $n")        if (n % 2 == 0) {(2)            'x:movie'(id: n, "the godfather $n (Extended)")        }    }}def movies =        new XmlSlurper()                .parseText(xmlWriter.toString())                .declareNamespace(x: 'http://www.groovy-lang.org')assert movies.'x:movie'.size() == 4assert movies.'x:movie'*.text().every { name -> name.startsWith('the') }
1Generating elements from a range
2Using a conditional for creating a given element

Of course the instance of a builder can be passed as a parameter torefactor/modularize your code:

Mix code
def xmlWriter = new StringWriter()def xmlMarkup = new MarkupBuilder(xmlWriter)(1)Closure<MarkupBuilder> buildMovieList = { MarkupBuilder builder ->    (1..3).each { n ->        builder.'x:movie'(id: n, "the godfather $n")        if (n % 2 == 0) {            builder.'x:movie'(id: n, "the godfather $n (Extended)")        }    }    return builder}xmlMarkup.'x:movies'('xmlns:x': 'http://www.groovy-lang.org') {    buildMovieList(xmlMarkup)(2)}def movies =        new XmlSlurper()                .parseText(xmlWriter.toString())                .declareNamespace(x: 'http://www.groovy-lang.org')assert movies.'x:movie'.size() == 4assert movies.'x:movie'*.text().every { name -> name.startsWith('the') }
1In this case we’ve created a Closure to handle the creation of a list of movies
2Just using thebuildMovieList function when necessary
StreamingMarkupBuilder

The classgroovy.xml.StreamingMarkupBuilder is a builder class forcreating XML markup. This implementation uses agroovy.xml.streamingmarkupsupport.StreamingMarkupWriter to handleoutput.

Using StreamingMarkupBuilder
def xml = new StreamingMarkupBuilder().bind {(1)    records {        car(name: 'HSV Maloo', make: 'Holden', year: 2006) {(2)            country('Australia')            record(type: 'speed', 'Production Pickup Truck with speed of 271kph')        }        car(name: 'P50', make: 'Peel', year: 1962) {            country('Isle of Man')            record(type: 'size', 'Smallest Street-Legal Car at 99cm wide and 59 kg in weight')        }        car(name: 'Royale', make: 'Bugatti', year: 1931) {            country('France')            record(type: 'price', 'Most Valuable Car at $15 million')        }    }}def records = new XmlSlurper().parseText(xml.toString())(3)assert records.car.size() == 3assert records.car.find { it.@name == 'P50' }.country.text() == 'Isle of Man'
1Note thatStreamingMarkupBuilder.bind returns aWritableinstance that may be used to stream the markup to a Writer
2We’re capturing the output in a String to parse it again and checkthe structure of the generated XML withXmlSlurper.
MarkupBuilderHelper

Thegroovy.xml.MarkupBuilderHelper is, as its name reflects, ahelper forgroovy.xml.MarkupBuilder.

This helper normally can be accessed from within an instance of classgroovy.xml.MarkupBuilder or an instance ofgroovy.xml.StreamingMarkupBuilder.

This helper could be handy in situations when you may want to:

  • Produce a comment in the output

  • Produce an XML processing instruction in the output

  • Produce an XML declaration in the output

  • Print data in the body of the current tag, escaping XML entities

  • Print data in the body of the current tag

In bothMarkupBuilder andStreamingMarkupBuilder this helper isaccessed by the propertymkp:

Using MarkupBuilder’s 'mkp'
def xmlWriter = new StringWriter()def xmlMarkup = new MarkupBuilder(xmlWriter).rules {    mkp.comment('THIS IS THE MAIN RULE')(1)    rule(sentence: mkp.yield('3 > n'))(2)}(3)assert xmlWriter.toString().contains('3 &gt; n')assert xmlWriter.toString().contains('<!-- THIS IS THE MAIN RULE -->')
1Usingmkp to create a comment in the XML
2Usingmkp to generate an escaped value
3Checking both assumptions were true

Here is another example to show the use ofmkp property accessiblefrom within thebind method scope when usingStreamingMarkupBuilder:

Using StreamingMarkupBuilder’s 'mkp'
def xml = new StreamingMarkupBuilder().bind {    records {        car(name: mkp.yield('3 < 5'))(1)        car(name: mkp.yieldUnescaped('1 < 3'))(2)    }}assert xml.toString().contains('3 &lt; 5')assert xml.toString().contains('1 < 3')
1If we want to generate an escaped value for the name attribute withmkp.yield
2Checking the values later on withXmlSlurper
DOMToGroovy

Suppose we have an existing XML document and we want to automategeneration of the markup without having to type it all in? We justneed to useorg.codehaus.groovy.tools.xml.DOMToGroovy as shown inthe following example:

Building MarkupBuilder from DOMToGroovy
def songs = """    <songs>      <song>        <title>Here I go</title>        <band>Whitesnake</band>      </song>    </songs>"""def builder =        javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder()def inputStream = new ByteArrayInputStream(songs.bytes)def document = builder.parse(inputStream)def output = new StringWriter()def converter = new DomToGroovy(new PrintWriter(output))(1)converter.print(document)(2)String xmlRecovered =        new GroovyShell()                .evaluate("""           def writer = new StringWriter()           def builder = new groovy.xml.MarkupBuilder(writer)           builder.${output}           return writer.toString()        """)(3)assert new XmlSlurper().parseText(xmlRecovered).song.title.text() == 'Here I go'(4)
1CreatingDOMToGroovy instance
2Converts the XML toMarkupBuilder calls which are available in the outputStringWriter
3Usingoutput variable to create the whole MarkupBuilder
4Back to XML string

3.11.4. Manipulating XML

In this chapter you’ll see the different ways of adding / modifying /removing nodes usingXmlSlurper orXmlParser. The xml we are goingto be handling is the following:

def xml = """<response version-api="2.0">    <value>        <books>            <book>                <title>Don Quixote</title>                <author>Miguel de Cervantes</author>            </book>        </books>    </value></response>"""
Adding nodes

The main difference betweenXmlSlurper andXmlParser is that whenformer creates the nodes they won’t be available until the document’sbeen evaluated again, so you should parse the transformed documentagain in order to be able to see the new nodes. So keep that in mindwhen choosing any of both approaches.

If you needed to see a node right after creating it thenXmlParsershould be your choice, but if you’re planning to do many changes tothe XML and send the result to another process maybeXmlSlurper wouldbe more efficient.

You can’t create a new node directly using theXmlSlurper instance,but you can withXmlParser. The way of creating a new node fromXmlParser is through its methodcreateNode(..)

def parser = new XmlParser()def response = parser.parseText(xml)def numberOfResults = parser.createNode(        response,        new QName("numberOfResults"),        [:])numberOfResults.value = "1"assert response.numberOfResults.text() == "1"

ThecreateNode() method receives the following parameters:

  • parent node (could be null)

  • The qualified name for the tag (In this case we only use the localpart without any namespace). We’re using an instance ofgroovy.namespace.QName

  • A map with the tag’s attributes (None in this particular case)

Anyway you won’t normally be creating a node from the parser instancebut from the parsed XML instance. That is from aNode or aGPathResult instance.

Take a look at the next example. We are parsing the xml withXmlParserand then creating a new node from the parsed document’s instance(Notice the method here is slightly different in the way it receivesthe parameters):

def parser = new XmlParser()def response = parser.parseText(xml)response.appendNode(        new QName("numberOfResults"),        [:],        "1")response.numberOfResults.text() == "1"

When usingXmlSlurper,GPathResult instances don’t havecreateNode()method.

Modifying / Removing nodes

We know how to parse the document, add new nodes, now I want to changea given node’s content. Let’s start usingXmlParser andNode. Thisexample changes the first book information to actually another book.

def response = new XmlParser().parseText(xml)/* Use the same syntax as groovy.xml.MarkupBuilder */response.value.books.book[0].replaceNode {(1)    book(id: "3") {        title("To Kill a Mockingbird")        author(id: "3", "Harper Lee")    }}def newNode = response.value.books.book[0]assert newNode.name() == "book"assert newNode.@id == "3"assert newNode.title.text() == "To Kill a Mockingbird"assert newNode.author.text() == "Harper Lee"assert newNode.author.@id.first() == "3"

When usingreplaceNode() the closure we pass as parameter shouldfollow the same rules as if we were usinggroovy.xml.MarkupBuilder:

Here’s the same example usingXmlSlurper:

def response = new XmlSlurper().parseText(books)/* Use the same syntax as groovy.xml.MarkupBuilder */response.value.books.book[0].replaceNode {    book(id: "3") {        title("To Kill a Mockingbird")        author(id: "3", "Harper Lee")    }}assert response.value.books.book[0].title.text() == "Don Quixote"/* That mkp is a special namespace used to escape away from the normal building mode   of the builder and get access to helper markup methods   'yield', 'pi', 'comment', 'out', 'namespaces', 'xmlDeclaration' and   'yieldUnescaped' */def result = new StreamingMarkupBuilder().bind { mkp.yield response }.toString()def changedResponse = new XmlSlurper().parseText(result)assert changedResponse.value.books.book[0].title.text() == "To Kill a Mockingbird"

Notice how usingXmlSlurper we have to parse the transformed documentagain in order to find the created nodes. In this particular examplecould be a little bit annoying isn’t it?

Finally both parsers also use the same approach for adding a newattribute to a given attribute. This time again the difference iswhether you want the new nodes to be available right away ornot. FirstXmlParser:

def parser = new XmlParser()def response = parser.parseText(xml)response.@numberOfResults = "1"assert response.@numberOfResults == "1"

AndXmlSlurper:

def response = new XmlSlurper().parseText(books)response.@numberOfResults = "2"assert response.@numberOfResults == "2"

When usingXmlSlurper, adding a new attribute doesnot require you to perform a new evaluation.

Printing XML
XmlUtil

Sometimes is useful to get not only the value of a given node but thenode itself (for instance to add this node to another XML).

For that you can usegroovy.xml.XmlUtil class. It has several staticmethods to serialize the xml fragment from several type of sources(Node, GPathResult, String…​)

Getting a node as a string
def response = new XmlParser().parseText(xml)def nodeToSerialize = response.'**'.find { it.name() == 'author' }def nodeAsText = XmlUtil.serialize(nodeToSerialize)assert nodeAsText ==        XmlUtil.serialize('<?xml version="1.0" encoding="UTF-8"?><author>Miguel de Cervantes</author>')

3.12. Processing YAML

Groovy has an optionalgroovy-yaml module which provides support for converting between Groovy objects and YAML. The classes dedicated toYAML serialisation and parsing are found in thegroovy.yaml package.

3.12.1. YamlSlurper

YamlSlurper is a class that parses YAML text or reader content into Groovy data structures (objects) such as maps, lists andprimitive types likeInteger,Double,Boolean andString.

The class comes with a bunch of overloadedparse methods plus some special methods such asparseTextand others. For the next example we will use theparseText method. It parses a YAMLString and recursively converts it to alist or map of objects. The otherparse* methods are similar in that they return a YAMLString but for different parametertypes.

        def ys = new YamlSlurper()        def yaml = ys.parseText '''language: groovysudo: requireddist: trustymatrix:  include:    - jdk: openjdk10    - jdk: oraclejdk9    - jdk: oraclejdk8before_script:  - |    unset _JAVA_OPTIONS        '''        assert 'groovy' == yaml.language        assert 'required' == yaml.sudo        assert 'trusty' == yaml.dist        assert ['openjdk10', 'oraclejdk9', 'oraclejdk8'] ==  yaml.matrix.include.jdk        assert ['unset _JAVA_OPTIONS'] == yaml.before_script*.trim()

Notice the result is a plain map and can be handled like a normal Groovy object instance.YamlSlurper parses thegiven YAML as defined by theYAML Ain’t Markup Language (YAML™).

AsYamlSlurper is returning pure Groovy object instances without any special YAML classes in the back, its usageis transparent. In fact,YamlSlurper results conform to GPath expressions. GPath is a powerful expression languagethat is supported by multiple slurpers for different data formats (XmlSlurper for XML being one example).

For more details please have a look at the section onGPath expressions.

The following table gives an overview of the YAML types and the corresponding Groovy data types:

YAMLGroovy

string

java.lang.String

number

java.lang.BigDecimal orjava.lang.Integer

object

java.util.LinkedHashMap

array

java.util.ArrayList

true

true

false

false

null

null

date

java.util.Date based on theyyyy-MM-dd’T’HH:mm:ssZ date format

Whenever a value in YAML isnull,YamlSlurper supplements it with the Groovynull value. This is in contrast to otherYAML parsers that represent anull value with a library-provided singleton object.
Builders

Another way to create YAML from Groovy is to useYamlBuilder. The builder provide aDSL which allows to formulate an object graph which is then converted to YAML.

        def builder = new YamlBuilder()        builder.records {            car {                name 'HSV Maloo'                make 'Holden'                year 2006                country 'Australia'                homepage new URL('http://example.org')                record {                    type 'speed'                    description 'production pickup truck with speed of 271kph'                }            }        }        assert builder.toString() == '''---records:  car:    name: "HSV Maloo"    make: "Holden"    year: 2006    country: "Australia"    homepage: "http://example.org"    record:      type: "speed"      description: "production pickup truck with speed of 271kph"'''

3.13. Processing TOML

Groovy has an optionalgroovy-toml module which provides support for converting between Groovy objects and TOML. The classes dedicated toTOML serialisation and parsing are found in thegroovy.toml package.

3.13.1. TomlSlurper

TomlSlurper is a class that parses TOML text or reader content into Groovy data structures (objects) such as maps, lists andprimitive types likeInteger,Double,Boolean andString.

The class comes with a bunch of overloadedparse methods plus some special methods such asparseTextand others. For the next example we will use theparseText method. It parses a TOMLString and recursively converts it to alist or map of objects. The otherparse* methods are similar in that they return a TOMLString but for different parametertypes.

        def ts = new TomlSlurper()        def toml = ts.parseText '''language = "groovy"sudo = "required"dist = "trusty"before_script = [ "unset _JAVA_OPTIONS\\n\\n    \\n" ][[matrix.include]]jdk = "openjdk10"[[matrix.include]]jdk = "oraclejdk9"[[matrix.include]]jdk = "oraclejdk8"'''        assert 'groovy' == toml.language        assert 'required' == toml.sudo        assert 'trusty' == toml.dist        assert ['openjdk10', 'oraclejdk9', 'oraclejdk8'] ==  toml.matrix.include.jdk        assert ['unset _JAVA_OPTIONS'] == toml.before_script*.trim()

Notice the result is a plain map and can be handled like a normal Groovy object instance.TomlSlurper parses thegiven TOML as defined by theTom’s Obvious, Minimal Language.

AsTomlSlurper is returning pure Groovy object instances without any special TOML classes in the back, its usageis transparent. In fact,TomlSlurper results conform to GPath expressions. GPath is a powerful expression languagethat is supported by multiple slurpers for different data formats (XmlSlurper for XML being one example).

For more details please have a look at the section onGPath expressions.

The following table gives an overview of the TOML types and the corresponding Groovy data types:

TOMLGroovy

string

java.lang.String

number

java.lang.BigDecimal orjava.lang.Integer

object

java.util.LinkedHashMap

array

java.util.ArrayList

true

true

false

false

null

null

date

java.util.Date based on theyyyy-MM-dd’T’HH:mm:ssZ date format

Whenever a value in TOML isnull,TomlSlurper supplements it with the Groovynull value. This is in contrast to otherTOML parsers that represent anull value with a library-provided singleton object.
Builders

Another way to create TOML from Groovy is to useTomlBuilder. The builder provide aDSL which allows to formulate an object graph which is then converted to TOML.

        def builder = new TomlBuilder()        builder.records {            car {                name 'HSV Maloo'                make 'Holden'                year 2006                country 'Australia'                homepage new URL('http://example.org')                record {                    type 'speed'                    description 'production pickup truck with speed of 271kph'                }            }        }        assert builder.toString() == '''\records.car.name = 'HSV Maloo'records.car.make = 'Holden'records.car.year = 2006records.car.country = 'Australia'records.car.homepage = 'http://example.org'records.car.record.type = 'speed'records.car.record.description = 'production pickup truck with speed of 271kph''''

3.14. Groovy Contracts – design by contract support for Groovy

This module provides contract annotations that support the specification of class-invariants,pre- and post-conditions on Groovy classes and interfaces.Special support is provided so that post-conditions may refer to the old value of variablesor to the result value associated with calling a method.

3.14.1. Applying @Invariant, @Requires and @Ensures

With GContracts in your class-path, contracts can be applied on a Groovy class or interface by using one of the assertions found in package org.gcontracts.annotations.

package acmeimport groovy.contracts.*@Invariant({ speed() >= 0 })class Rocket {    int speed = 0    boolean started = true    @Requires({ isStarted() })    @Ensures({ old.speed < speed })    def accelerate(inc) { speed += inc }    def isStarted() { started }    def speed() { speed }}def r = new Rocket()r.accelerate(5)

3.14.2. More Features

GContracts supports the following feature set:

  • definition of class invariants, pre- and post-conditions via @Invariant, @Requires and @Ensures

  • inheritance of class invariants, pre- and post-conditions of concrete predecessor classes

  • inheritance of class invariants, pre- and post-conditions in implemented interfaces

  • usage of old and result variable in post-condition assertions

  • assertion injection in Plain Old Groovy Objects (POGOs)

  • human-readable assertion messages, based on Groovy power asserts

  • enabling contracts at package- or class-level with @AssertionsEnabled

  • enable or disable contract checking with Java’s -ea and -da VM parameters

  • annotation contracts: a way to reuse reappearing contract elements in a project domain model

  • detection of circular assertion method calls

3.14.3. The Stack Example

Currently, Groovy contracts supports 3 annotations: @Invariant, @Requires and @Ensures – all of them workas annotations with closures, where closures allow you to specify arbitrary code pieces as annotation parameters:

@Grab(group='org.apache.groovy', module='groovy-contracts', version='4.0.0')import groovy.contracts.*@Invariant({ elements != null })class Stack<T> {    List<T> elements    @Ensures({ is_empty() })    def Stack()  {        elements = []    }    @Requires({ preElements?.size() > 0 })    @Ensures({ !is_empty() })    def Stack(List<T> preElements)  {        elements = preElements    }    boolean is_empty()  {        elements.isEmpty()    }    @Requires({ !is_empty() })    T last_item()  {        elements.get(count() - 1)    }    def count() {        elements.size()    }    @Ensures({ result == true ? count() > 0 : count() >= 0  })    boolean has(T item)  {        elements.contains(item)    }    @Ensures({ last_item() == item })    def push(T item)  {       elements.add(item)    }    @Requires({ !is_empty() })    @Ensures({ last_item() == item })    def replace(T item)  {        remove()        elements.add(item)    }    @Requires({ !is_empty() })    @Ensures({ result != null })    T remove()  {        elements.remove(count() - 1)    }    String toString() { elements.toString() }}def stack = new Stack<Integer>()

The example above specifies a class-invariant and methods with pre- and post-conditions.Note, that preconditions may reference method arguments and post-conditions have accessto the method’s result with the result variable and old instance variables values with old.

Indeed, Groovy AST transformations change these assertion annotations into Java assertionstatements (can be turned on and off with a JVM param) and inject them at appropriate places,e.g. class-invariants are used to check an object’s state before and after each method call.

3.15. Scripting Ant tasks

Groovy integrates very well withApache Ant thanks toAntBuilder.

3.16. The <groovy> Ant Task

3.16.1.<groovy>

Here we describe an Ant task for using Groovyfrom within an Ant build file.You may also be interested inAnt’s built-inscript taskwhich supports Groovy and other languages, orAntBuilder which lets you write Ant build scriptsin Groovy rather than XML.

Executes a series of Groovy statements fromApache Ant.Statements can either be read in from a resource or as direct text between the enclosing Groovy tags.

3.16.2. Required taskdef

Assuming all the groovy jars you need are inmy.classpath (this will begroovy-VERSION.jar,groovy-ant-VERSION.jar plus any modules and transitive dependencies you might be using)you will need to declare this task at some point in thebuild.xml prior tothegroovy task being invoked.

<taskdef name="groovy"         classname="org.codehaus.groovy.ant.Groovy"         classpathref="my.classpath"/>

You can simply place statements between thegroovy tags like this:

<groovy>...</groovy>

Or you can supply the Groovy source script as a resource. You can specify the pathname using thesrc attribute like this:

<groovy src="/some/path/MyGroovyScript.groovy" otherAttributes="...">

Or as a nestedfileset like this (though the fileset definition is expected to select just one file):

<groovy>    <fileset file="MyGroovyScript.groovy"/></groovy>

Or as a nested single elementresource collection which could look like any of these:

<groovy>    <file file="MyGroovyScript.groovy"/></groovy><groovy>    <url url="https://some.domain/some/path/to/MyGroovyScript.groovy"/></groovy><groovy>    <javaconstant name="some.packagename.SomeClass.MY_CODE_FRAGMENT"/></groovy>

You may also supply afilter chain like this:

<groovy>    <fileset file="MyGroovyScript.groovy"/>    <!-- take 5 lines after skipping 18 lines, just as an example -->    <filterchain>        <headfilter lines="5" skip="18"/>    </filterchain></groovy>

You might need to use thecontextClassLoader attribute (see below) if any of your modules load services via the classpath, e.g.groovy-json.

3.16.3. <groovy> attributes

AttributeDescriptionRequired

src

File containing Groovy statements. The directory containing the file is added to the classpath.

Yes, unless statements enclosed within tags

classpath

The classpath to use.

No

classpathref

The classpath to use, given as reference to a PATH defined elsewhere.

No

output

Set the output file; defaults to the Ant log.

No

append

If enabled and output is to a file, append to existing file rather than overwrite. Defaults to false.

No

fork

If enabled the script will be executed in a forked JVM process (disabled by default).

No

scriptBaseClass

The name of the base class for scripts.

No

parameters

Generates metadata for reflection on method parameter names on JDK 8 and above. Defaults to false.

No

useGroovyShell

If enabled a new GroovyShell is used to run the script. Special variables won’t be available but you don’t need Ant in the classpath. Defaults to false.

No

includeAntRuntime

If enabled the system classpath will be included on the classpath when forking. Defaults to true.

No

stacktrace

If enabled a stacktrace will be reported if an error occurs during compilation. Defaults to false.

No

configScript

Sets the configuration script for the groovy compiler configuration.

No

contextClassLoader

If enabled, the contextClassLoader to be set with the classLoader of the shell used to run the script. Not used if fork is true.

No

3.16.4. Parameters specified as nested elements

<classpath>

Groovy’s classpath attribute is a PATH like structure and can also be set via a nested classpath element.

<arg>

Arguments can be set via one or more nested <arg> elements using the standard Antcommand line conventions.

3.16.5. Available bindings

A number of bindings are in scope for use within your Groovy statements.

NameDescription

ant

an instance ofAntBuilder that knows about the current ant project

project

the current ant project

properties

aMap of ant properties

target

the owning target that invoked this groovy script

task

the wrapping task, can access anything needed inorg.apache.tools.ant.Task

args

command line arguments, if any

3.16.6. Examples

Hello world, version 1:

<groovy>println "Hello World"</groovy>

Hello world, version 2:

<groovy>ant.echo "Hello World"</groovy>

List all xml files in the current directory:

<groovy>xmlfiles = new File(".").listFiles().findAll{ it =~ "\.xml$" }xmlfiles.sort().each { println it.toString() }</groovy>

List all xml files within a jar:

<zipfileset src="foobar.jar"            includes="**/*.xml"/><groovy>    project.references.found.each {        println it.name    }</groovy>

Run a script:

<groovy src="/some/directory/some/file.groovy">  <classpath>    <pathelement location="/my/groovy/classes/directory"/>  </classpath></groovy>

Find allBuilder classes having anorg.* package within a directory of jars:

<property name="local.target" value="C:/Projects/GroovyExamples"/><groovy>import java.util.jar.JarFiledef classes = []def resourceNamePattern = /org\/.*\/.*Builder.class/def jarNamePattern = /.*(beta|commons).*jar$/def libdir = new File("${properties['local.target']}/lib")libdir.listFiles().grep(~jarNamePattern).each { candidate ->    new JarFile(candidate).entries().each { entry ->        if (entry.name ==~ resourceNamePattern) classes += entry.name    }}properties["builder-classes"] = classes.join(' ')</groovy><echo message='${builder-classes}'/>

Which might result in something like:

org/apache/commons/cli/PatternOptionBuilder.class org/apache/commons/cli/OptionBuilder.class org/codehaus/groovy/tools/groovydoc/GroovyRootDocBuilder.class org/custommonkey/xmlunit/HTMLDocumentBuilder.class org/custommonkey/xmlunit/TolerantSaxDocumentBuilder.class

FileScanner version of above (with a slight variation on collecting the names):

<groovy>import java.util.jar.JarFiledef resourceNamePattern = /org\/.*\/.*Builder.class/def candidates = ant.fileScanner {    fileset(dir: '${local.target}/lib') {        include(name: '*beta*.jar')        include(name: '*commons*.jar')    }}def classes = candidates.collect {    new JarFile(it).entries().collect { it.name }.findAll {        it ==~ resourceNamePattern    }}.flatten()properties["builder-classes"] = classes.join(' ')</groovy>

Calling out to a web service from your Ant script:

<?xml version="1.0" encoding="UTF-8"?><project name="SOAP example" default="main" basedir=".">    <property environment="env"/>    <property name="celsius" value="0"/>    <target name="main">        <taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy">            <classpath>                <fileset dir="${env.GROOVY_HOME}" includes="lib/groovy-*.jar,lib/ivy*.jar"/>            </classpath>        </taskdef>        <groovy>            @Grab('org.codehaus.groovy.modules:groovyws:0.5.1')            import groovyx.net.ws.WSClient            def url = 'http://www.w3schools.com/webservices/tempconvert.asmx?WSDL'            def proxy = new WSClient(url, this.class.classLoader)            proxy.initialize()            ant.echo "I'm freezing at ${properties.celsius} degrees Celsius"            properties.result = proxy.CelsiusToFahrenheit(properties.celsius)        </groovy>        <antcall/>    </target>    <target name="results">        <echo message="I'm freezing at ${result} degrees Fahrenheit"/>    </target></project>

Which will output the following (along with some informational messages):

main:     ...     [echo] I'm freezing at 0 degrees Celsiusresults:     [echo] I'm freezing at 32 degrees FahrenheitBUILD SUCCESSFUL

Setting arguments:

<target name="run">    <groovy>        <arg line="1 2 3"/>        <arg value="4 5"/>        println args.size()        println args[2]        args.each{ ant.echo(message:it) }    </groovy></target>

Output:

Buildfile: build.xmlrun:   [groovy] 4   [groovy] 3     [echo] 1     [echo] 2     [echo] 3     [echo] 4 5BUILD SUCCESSFUL

3.17. The <groovyc> Ant Task

3.17.1.<groovyc>

Description

Compiles Groovy source files and, if joint compilation option is used, Java source files fromApache Ant.

Required taskdef

Assuming the groovy jars are ingroovy.libs, you will need to declare this taskat some point in thebuild.xml prior to thegroovyc task being invoked.Consider also adding any additional Groovy module jars, libraries and potentially transitive dependencies you might be using.

<taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc">  <classpath>    <fileset file="${groovy.libs}/groovy-ant-VERSION.jar"/>    <fileset file="${groovy.libs}/groovy-VERSION.jar"/>  </classpath></taskdef>
<groovyc> Attributes
AttributeDescriptionRequired

srcdir

Location of the Groovy (and possibly Java) source files.

Yes

destdir

Location to store the class files.

Yes

classpath

The classpath to use.

No

classpathref

The classpath to use given as a path references.

No

sourcepath

The sourcepath to use.

No

sourcepathref

The sourcepath to use given as a path reference.

No

encoding

Encoding of source files.

No

verbose

Asks the compiler for verbose output; defaults to no.

No

includeAntRuntime

Whether to include the Ant run-time libraries in theclasspath; defaults to yes.

No

includeJavaRuntime

Whether to include the default run-time librariesfrom the executing VM in the classpath; defaults to no.

No

includeDestClasses

This property controls whether to include the destinationclasses directory in the classpath given to the compiler. The default value is "true".

No

fork

Whether to execute groovyc using a spawned instance of the JVM;defaults to no.

No

memoryInitialSize

The initial size of the memory for the underlyingVM, if using fork mode; ignored otherwise. Defaults to the standard VMmemory setting. (Examples: 83886080, 81920k, or 80m)

No

memoryMaximumSize

The maximum size of the memory for the underlyingVM, if using fork mode; ignored otherwise. Defaults to the standard VMmemory setting. (Examples: 83886080, 81920k, or 80m)

No

failonerror

Indicates whether compilation errors will fail the build;defaults to true.

No

proceed

Inverse alias forfailonerror.

No

listfiles

Indicates whether the source files to be compiled will belisted; defaults to no.

No

stacktrace

if true each compile error message will contain astacktrace

No

indy

Enable compilation with the ``invoke dynamic'' support when usingGroovy 2.0 and beyond and running on JDK 7

No

scriptBaseClass

Sets the base class for Groovy scripts

No

stubdir

Set the stub directory into which the Java source stub files should be generated.The directory need not exist and will not be deleted automatically - though its contentswill be cleared unless 'keepStubs' is true. Ignored when forked.

No

keepStubs

Set the keepStubs flag. Defaults to false. Set to true for debugging.Ignored when forked.

No

forceLookupUnnamedFiles

The Groovyc Ant task is frequently used in the context of a build systemthat knows the complete list of source files to be compiled. In such acontext, it is wasteful for the Groovy compiler to go searching theclasspath when looking for source files and hence by default theGroovyc Ant task calls the compiler in a special mode with such searchingturned off. If you wish the compiler to search for source files thenyou need to set this flag to true. Defaults to false.

No

configscript

Set the configuration file used to customize the compilation configuration.

No

parameters

Generates metadata for reflection on method parameter names on JDK 8 and above.Defaults to false.

No

previewFeatures

Enables the JEP preview features on JDK 12 and above.Defaults to false.

No

targetBytecode

Sets the bytecode compatibility level.

No

javahome

Sets thejava.home value to use, default is the current JDK’s home.

No

executable

Sets the name of the java executable to use when invoking the compiler in forked mode,ignored otherwise.

No

scriptExtension

Set the extension to use when searching for Groovy source files.Accepts extensions in the form *.groovy, .groovy or groovy.

No

updatedProperty

The property to set on compilation success. This property will not be set ifthe compilation fails, or if there are no files to compile.

No

errorProperty

The property to set on compilation failure. This property will be set ifthe compilation fails.

No

Example:

<path>  <fileset dir="${groovy.libs}" includes="*.jar" excludes="groovy-ant-*.jar"/>  ...</path><groovyc srcdir="${dir.sources}" destdir="${dir.classes}" classpathref="classpath.main"         fork="true" includeantruntime="false" configscript="config.groovy" targetBytecode="1.8"/>
<groovyc> Nested Elements
elementkindRequiredReplaces Attribute

src

a path structure

Yes (unless srcdir is used)

srcdir

classpath

a path structure

No

classpath or classpathref

javac

javac task

No

N/A

Notes:

  • For path structures see for examplehttps://ant.apache.org/manual/using.html#path

  • For usages of thejavac task seehttps://ant.apache.org/manual/Tasks/javac.html

  • The nestedjavac task behaves more or less as documented for the top-leveljavac task.srcdir,destdir,fork,memoryInitialSize, andmemoryMaximumSizefor the nestedjavac task are taken from the enclosinggroovyc task.If these attributes or any else that are not explicitly supported are specified then awarning is logged, and they are ignored completely.classpath andclasspathref specified on the nestedjavac task is merged withthe values taken from the enclosinggroovyc task and also used for the Groovy compilation.Nested inside the nestedjavac task the only element supported iscompilerarg,and this only with thevalue attribute, which is treated like theline attribute of thetop-leveljavac task, i.e. it is split by spaces into separate arguments.Only arguments starting with-W,-X, or-proc: are properly translated as needed.Anything else is supplied as-is to groovyc and has to be manually prefixed with-F or-J.

Joint Compilation

Joint compilation is enabled by using an embeddedjavac element, as shown in the following example:

<groovyc srcdir="${testSourceDirectory}" destdir="${testClassesDirectory}" targetBytecode="1.8">  <classpath>    <pathelement path="${mainClassesDirectory}"/>    <pathelement path="${testClassesDirectory}"/>    <path refid="testPath"/>  </classpath>  <javac debug="true" source="1.8" /></groovyc>

More details about joint compilation can be found in thejoint compilation section.

3.18. Template engines

3.18.1. Introduction

Groovy supports multiple ways to generate text dynamically includingGStrings,printf andMarkupBuilder just to name a few.In addition to these, there is a dedicated template framework which is well-suited to applications where the text to be generated follows the form of a static template.

3.18.2. Template framework

The template framework in Groovy consists of aTemplateEngine abstract base class that engines must implementand aTemplate interface that the resulting templates they generate must implement.

Included with Groovy are several template engines:

  • SimpleTemplateEngine - for basic templates

  • StreamingTemplateEngine - functionally equivalent toSimpleTemplateEngine, but can handle strings larger than 64k

  • GStringTemplateEngine - stores the template as writeable closures (useful for streaming scenarios)

  • XmlTemplateEngine - works well when the template and output are valid XML

  • MarkupTemplateEngine - a very complete, optimized, template engine

3.18.3. SimpleTemplateEngine

Shown here is theSimpleTemplateEngine that allows you to use JSP-like scriptlets (see example below), script, and EL expressionsin your template in order to generate parametrized text. Here is an example of using the system:

def text = 'Dear "$firstname $lastname",\nSo nice to meet you in <% print city %>.\nSee you in ${month},\n${signed}'def binding = ["firstname":"Sam", "lastname":"Pullara", "city":"San Francisco", "month":"December", "signed":"Groovy-Dev"]def engine = new groovy.text.SimpleTemplateEngine()def template = engine.createTemplate(text).make(binding)def result = 'Dear "Sam Pullara",\nSo nice to meet you in San Francisco.\nSee you in December,\nGroovy-Dev'assert result == template.toString()

While it is generally not deemed good practice to mix processing logic in your template (or view), sometimes very simple logic can be useful.E.g. in the example above, we could change this:

$firstname

to this (assuming we have set up a static import for capitalizeinside the template):

${firstname.capitalize()}

or this:

<% print city %>

to this:

<% print city == "New York" ? "The Big Apple" : city %>
Advanced Usage Note

If you happen to be embedding your template directly in your script (as we did above) you have to be careful about backslash escaping.Because the template string itself will be parsed by Groovy before it is passed to the templating framework, you have toescape any backslashes inside GString expressions or scriptlet 'code' that are entered as part of a Groovy program.E.g. if we wanted quotes aroundThe Big Apple above, we would use:

<% print city == "New York" ? "\\"The Big Apple\\"" : city %>

Similarly, if we wanted a newline, we would use:

\\n

in any GString expression or scriptlet 'code' that appears inside a Groovy script. A normal “\n” is fine withinthe static template text itself or if the entire template itself is in an external template file.Similarly, to represent an actual backslash in your text you would need

\\\\

in an external file or

\\\\

in any GString expression or scriptlet 'code'. (Note: the necessity to have this extra slash may go awayin a future version of Groovy if we can find an easy way to support such a change.)

3.18.4. StreamingTemplateEngine

TheStreamingTemplateEngine engine is functionally equivalent to theSimpleTemplateEngine,but creates the template using writable closures making it more scalable for large templates.Specifically this template engine can handle strings larger than 64k.

It uses JSP style <% %> script and <%= %> expression syntax or GString style expressions.The variable 'out' is bound to the writer that the template is being written to.

Frequently, the template source will be a file but here we show a simple example providing the template as a string:

def text = '''\Dear <% out.print firstname %> ${lastname},We <% if (accepted) out.print 'are pleased' else out.print 'regret' %> \to inform you that your paper entitled'$title' was ${ accepted ? 'accepted' : 'rejected' }.The conference committee.'''def template = new groovy.text.StreamingTemplateEngine().createTemplate(text)def binding = [    firstname : "Grace",    lastname  : "Hopper",    accepted  : true,    title     : 'Groovy for COBOL programmers']String response = template.make(binding)assert response == '''Dear Grace Hopper,We are pleased to inform you that your paper entitled'Groovy for COBOL programmers' was accepted.The conference committee.'''

3.18.5. GStringTemplateEngine

As an example of using theGStringTemplateEngine, here is the example above done again (with a few changes to show some other options).First we will store the template in a file this time:

test.template
Dear "$firstname $lastname",So nice to meet you in <% out << (city == "New York" ? "\\"The Big Apple\\"" : city) %>.See you in ${month},${signed}

Note that we usedout instead ofprint to support the streaming nature ofGStringTemplateEngine.Because we have the template in a separate file, there is no need to escape the backslashes. Here is how we call it:

def f = new File('test.template')def engine = new groovy.text.GStringTemplateEngine()def template = engine.createTemplate(f).make(binding)println template.toString()

and here is the output:

Dear "Sam Pullara",So nice to meet you in "The Big Apple".See you in December,Groovy-Dev

3.18.6. XmlTemplateEngine

XmlTemplateEngine for use in templating scenarios where both the template source and the expected output are intended to be XML.Templates may use the normal${expression} and$variable notations to insert an arbitrary expression into the template.In addition, support is also provided for special tags:<gsp:scriptlet> (for inserting code fragments) and<gsp:expression>(for code fragments which produce output).

Comments and processing instructions will be removed as part of processing and special XML characters suchas<,>," and' will be escaped using the respective XML notation.The output will also be indented using standard XML pretty printing.

The xmlns namespace definition for gsp: tags will be removed but other namespace definitions will be preserved(but may change to an equivalent position within the XML tree).

Normally, the template source will be in a file but here is a simple example providing the XML template as a string:

def binding = [firstname: 'Jochen', lastname: 'Theodorou', nickname: 'blackdrag', salutation: 'Dear']def engine = new groovy.text.XmlTemplateEngine()def text = '''\    <document xmlns:gsp='http://groovy.codehaus.org/2005/gsp' xmlns:foo='baz' type='letter'>        <gsp:scriptlet>def greeting = "${salutation}est"</gsp:scriptlet>        <gsp:expression>greeting</gsp:expression>        <foo:to>$firstname "$nickname" $lastname</foo:to>        How are you today?    </document>'''def template = engine.createTemplate(text).make(binding)println template.toString()

This example will produce this output:

<document type='letter'>  Dearest  <foo:to xmlns:foo='baz'>    Jochen &quot;blackdrag&quot; Theodorou  </foo:to>  How are you today?</document>

3.18.7. The MarkupTemplateEngine

This template engine is a template engine primarily aimed at generating XML-like markup (XML, XHTML, HTML5, …​), but thatcan be used to generate any text based content. Unlike traditional template engines, this one relies on a DSL that uses thebuilder syntax. Here is a sample template:

xmlDeclaration()cars {   cars.each {       car(make: it.make, model: it.model)   }}

If you feed it with the following model:

model = [cars: [new Car(make: 'Peugeot', model: '508'), new Car(make: 'Toyota', model: 'Prius')]]

It would be rendered as:

<?xml version='1.0'?><cars><car make='Peugeot' model='508'/><car make='Toyota' model='Prius'/></cars>

The key features of this template engine are:

  • amarkup builder like syntax

  • templates are compiled into bytecode

  • fast rendering

  • optional type checking of the model

  • includes

  • internationalization support

  • fragments/layouts

The template format
Basics

Templates consist of Groovy code. Let’s explore the first example more thoroughly:

xmlDeclaration()(1)cars {(2)   cars.each {(3)       car(make: it.make, model: it.model)(4)   }(5)}
1renders the XML declaration string.
2opens acars tag
3cars is a variable found in thetemplate model, which is a list ofCar instances
4for each item, we create acar tag with the attributes from theCar instance
5closes thecars tag

As you can see, regular Groovy code can be used in the template. Here, we are callingeach on a list (retrieved from the model), allowing us torender onecar tag per entry.

In a similar fashion, rendering HTML code is as simple as this:

yieldUnescaped '<!DOCTYPE html>'(1)html(lang:'en') {(2)    head {(3)        meta('http-equiv':'"Content-Type" content="text/html; charset=utf-8"')(4)        title('My page')(5)    }(6)    body {(7)        p('This is an example of HTML contents')(8)    }(9)}(10)
1renders the HTML doctype special tag
2opens thehtml tag with an attribute
3opens thehead tag
4renders ameta tag with onehttp-equiv attribute
5renders thetitle tag
6closes thehead tag
7opens thebody tag
8renders ap tag
9closes thebody tag
10closes thehtml tag

The output is straightforward:

<!DOCTYPE html><html lang='en'><head><meta http-equiv='"Content-Type" content="text/html; charset=utf-8"'/><title>My page</title></head><body><p>This is an example of HTML contents</p></body></html>
With someconfiguration, you can have the output pretty printed, with newlines and indent automatically added.
Support methods

In the previous example, the doctype declaration was rendered using theyieldUnescaped method. We have also seen thexmlDeclaration method.The template engine provides several support methods that will help you render contents appropriately:

MethodDescriptionExample

yield

Renders contents, but escapes it before rendering

Template:

yield 'Some text with <angle brackets>'

Output:

Some text with &lt;angle brackets&gt;

yieldUnescaped

Renders raw contents. The argument is rendered as is, without escaping.

Template:

yieldUnescaped 'Some text with <angle brackets>'

Output:

Some text with <angle brackets>

xmlDeclaration

Renders an XML declaration String. If the encoding is specified in the configuration, it is written in the declaration.

Template:

xmlDeclaration()

Output:

<?xml version='1.0'?>

IfTemplateConfiguration#getDeclarationEncoding is not null:

Output:

<?xml version='1.0' encoding='UTF-8'?>

comment

Renders raw contents inside an XML comment

Template:

comment 'This is <a href="foo.html">commented out</a>'

Output:

<!--This is <a href="foo.html">commented out</a>-->

newLine

Renders a new line. See alsoTemplateConfiguration#setAutoNewLine andTemplateConfiguration#setNewLineString.

Template:

p('text')newLine()p('text on new line')

Output:

<p>text</p><p>text on new line</p>

pi

Renders an XML processing instruction.

Template:

pi("xml-stylesheet":[href:"mystyle.css", type:"text/css"])

Output:

<?xml-stylesheet href='mystyle.css' type='text/css'?>

tryEscape

Returns an escaped string for an object, if it is aString (or any type derived fromCharSequence). Otherwise returns the object itself.

Template:

yieldUnescaped tryEscape('Some text with <angle brackets>')

Output:

Some text with &lt;angle brackets&gt;
Includes

TheMarkupTemplateEngine supports inclusion of contents from another file. Included contents may be:

  • another template

  • raw contents

  • contents to be escaped

Including another template can be done using:

include template: 'other_template.tpl'

Including a file as raw contents, without escaping it, can be done like this:

include unescaped: 'raw.txt'

Eventually, inclusion of text that should be escaped before rendering can be done using:

include escaped: 'to_be_escaped.txt'

Alternatively, you can use the following helper methods instead:

  • includeGroovy(<name>) to include another template

  • includeEscaped(<name>) to include another file with escaping

  • includeUnescaped(<name>) to include another file without escaping

Calling those methods instead of theinclude xxx: syntax can be useful if the name of the file to be included is dynamic (stored in a variable for example).Files to be included (independently of their type, template or text) are found onclasspath. This is one of the reasons why theMarkupTemplateEngine takesan optionalClassLoader as constructor argument (the other reason being that you can include code referencing other classes in a template).

If you don’t want your templates to be on classpath, theMarkupTemplateEngine accepts a convenient constructor that lets you define the directory wheretemplates are to be found.

Fragments

Fragments are nested templates. They can be used to provide improved composition in a single template. A fragment consists ofa string, the inner template, and a model, used to render this template. Consider the following template:

ul {    pages.each {        fragment "li(line)", line:it    }}

Thefragment element creates a nested template, and renders it with a model which is specific to this template. Here,we have theli(line) fragment, whereline is bound toit. Sinceit corresponds to the iteration ofpages,we will generate a singleli element for each page in our model:

<ul><li>Page 1</li><li>Page 2</li></ul>

Fragments are interesting to factorize template elements. They come at the price of the compilation of a fragment per template, and they cannotbe externalized.

Layouts

Layouts, unlike fragments, refer to other templates. They can be used to compose templates and share common structures. This is ofteninteresting if you have, for example, a common HTML page setup, and that you only want to replace the body. This can be done easilywith alayout. First of all, you need to create a layout template:

layout-main.tpl
html {    head {        title(title)(1)    }    body {        bodyContents()(2)    }}
1thetitle variable (inside the title tag) is a layout variable
2thebodyContents call will render the body

Then what you need is a template that includes the layout:

layout 'layout-main.tpl',(1)    title: 'Layout example',(2)    bodyContents: contents { p('This is the body') }(3)
1use themain-layout.tpl layout file
2set thetitle variable
3set thebodyContents

As you can see,bodyContents will be rendered inside the layout, thanks to thebodyContents() call in the layout file. Asa result, the template will be rendered as this:

<html><head><title>Layout example</title></head><body><p>This is the body</p></body></html>

The call to thecontents method is used to tell the template engine that the block of code is in fact a specification of atemplate, instead of a helper function to be rendered directly. If you don’t addcontents before your specification, thenthe contents would be rendered, but you would also see a random string generated, corresponding to the result value of the block.

Layouts are a powerful way to share common elements across multipletemplates, without having to rewrite everything or use includes.

Layouts use, by default, a model which is independent from the model of the page where they are used. It is however possibleto make them inherit from the parent model. Imagine that the model is defined like this:

model = new HashMap<String,Object>();model.put('title','Title from main model');

and the following template:

layout 'layout-main.tpl', true,(1)    bodyContents: contents { p('This is the body') }
1note the use oftrue to enable model inheritance

then it is not necessary to pass thetitle value to the layout as in theprevious example. The result will be:

<html><head><title>Title from main model</title></head><body><p>This is the body</p></body></html>

But it is also possible to override a value from the parent model:

layout 'layout-main.tpl', true,(1)    title: 'overridden title',(2)    bodyContents: contents { p('This is the body') }
1true means inherit from the parent model
2buttitle is overridden

then the output will be:

<html><head><title>overridden title</title></head><body><p>This is the body</p></body></html>
Rendering contents
Creation of a template engine

On the server side, rendering templates require an instance ofgroovy.text.markup.MarkupTemplateEngine and agroovy.text.markup.TemplateConfiguration:

TemplateConfiguration config = new TemplateConfiguration();(1)MarkupTemplateEngine engine = new MarkupTemplateEngine(config);(2)Template template = engine.createTemplate("p('test template')");(3)Map<String, Object> model = new HashMap<>();(4)Writable output = template.make(model);(5)output.writeTo(writer);(6)
1creates a template configuration
2creates a template engine with this configuration
3creates a template instance from aString
4creates a model to be used in the template
5bind the model to the template instance
6render output

There are several possible options to parse templates:

  • from aString, usingcreateTemplate(String)

  • from aReader, usingcreateTemplate(Reader)

  • from aURL, usingcreateTemplate(URL)

  • given a template name, usingcreateTemplateByPath(String)

The last version should in general be preferred:

Template template = engine.createTemplateByPath("main.tpl");Writable output = template.make(model);output.writeTo(writer);
Configuration options

The behavior of the engine can be tweaked with several configuration options accessible through theTemplateConfiguration class:

OptionDefault valueDescriptionExample

declarationEncoding

null

Determines the value of the encoding to be written whenxmlDeclaration is called. It doesnot influence the writer you are using as output.

Template:

xmlDeclaration()

Output:

<?xml version='1.0'?>

IfTemplateConfiguration#getDeclarationEncoding is not null:

Output:

<?xml version='1.0' encoding='UTF-8'?>

expandEmptyElements

false

If true, empty tags are rendered in their expanded form.

Template:

p()

Output:

<p/>

IfexpandEmptyElements is true:

Output:

<p></p>

useDoubleQuotes

false

If true, use double quotes for attributes instead of simple quotes

Template:

tag(attr:'value')

Output:

<tag attr='value'/>

IfuseDoubleQuotes is true:

Output:

<tag attr="value"/>

newLineString

System default (system propertyline.separator)

Allows to choose what string is used when a new line is rendered

Template:

p('foo')newLine()p('baz')

IfnewLineString='BAR':

Output:

<p>foo</p>BAR<p>baz</p>

autoEscape

false

If true, variables from models are automatically escaped before rendering.

autoIndent

false

If true, performs automatic indentation after new lines

autoIndentString

four (4) spaces

The string to be used as indent.

autoNewLine

false

If true, performs automatic insertion of new lines

baseTemplateClass

groovy.text.markup.BaseTemplate

Sets the super class of compiled templates. This can be used to provide application specific templates.

locale

Default locale

Sets the default locale for templates.

Once the template engine has been created, it isunsafe to change the configuration.
Automatic formatting

By default, the template engine will render output without any specific formatting. Someconfiguration options can improve the situation:

  • autoIndent is responsible for auto-indenting after a new line is inserted

  • autoNewLine is responsible for automatically inserting new lines based on the original formatting of the template source

In general, it is recommended to set bothautoIndent andautoNewLine to true if you want human-readable, pretty printed, output:

config.setAutoNewLine(true);config.setAutoIndent(true);

Using the following template:

html {    head {        title('Title')    }}

The output will now be:

<html>    <head>        <title>Title</title>    </head></html>

We can slightly change the template so that thetitle instruction is found on the same line as thehead one:

html {    head { title('Title')    }}

And the output will reflect that:

<html>    <head><title>Title</title>    </head></html>

New lines areonly inserted where curly braces for tags are found, and the insertion corresponds to where the nested content is found. This means thattags in the body of another tag willnot trigger new lines unless they use curly braces themselves:

html {    head {        meta(attr:'value')(1)        title('Title')(2)        newLine()(3)        meta(attr:'value2')(4)    }}
1a new line is inserted becausemeta is not on the same line ashead
2no new line is inserted, because we’re on the same depth as the previous tag
3we can force rendering of a new line by explicitly callingnewLine
4and this tag will be rendered on a separate line

This time, the output will be:

<html>    <head>        <meta attr='value'/><title>Title</title>        <meta attr='value2'/>    </head></html>

By default, the renderer uses four(4) spaces as indent, but you can change it by setting theTemplateConfiguration#autoIndentString property.

Automatic escaping

By default, contents which is read from the model is renderedas is. If this contents comes from user input, it can be sensible, and you mightwant to escape it by default, for example to avoid XSS injection. For that, the template configuration provides an option which will automaticallyescape objects from the model, as long as they inherit fromCharSequence (typically, `String`s).

Let’s imagine the following setup:

config.setAutoEscape(false);model = new HashMap<String,Object>();model.put("unsafeContents", "I am an <html> hacker.");

and the following template:

html {    body {        div(unsafeContents)    }}

Then you wouldn’t want the HTML fromunsafeContents to be rendered as is, because of potential security issues:

<html><body><div>I am an <html> hacker.</div></body></html>

Automatic escaping will fix this:

config.setAutoEscape(true);

And now the output is properly escaped:

<html><body><div>I am an &lt;html&gt; hacker.</div></body></html>

Note that using automatic escaping doesn’t prevent you from including unescaped contents from the model. To do this, your template should then explicitlymention that a model variable should not be escaped by prefixing it withunescaped., like in this example:

Explicit bypass of automatic escaping
html {    body {        div(unescaped.unsafeContents)    }}
Common gotchas
Strings containing markup

Say that you want to generate a<p> tag which contains a string containing markup:

p {    yield "This is a "    a(href:'target.html', "link")    yield " to another page"}

and generates:

<p>This is a <a href='target.html'>link</a> to another page</p>

Can’t this be written shorter? A naive alternative would be:

p {    yield "This is a ${a(href:'target.html', "link")} to another page"}

but the result will not look as expected:

<p><a href='target.html'>link</a>This is a  to another page</p>

The reason is that the markup template engine is astreaming engine. In the original version, the firstyield callgenerates a string which is streamed to the output, then thea link is generated and streamed, and then the lastyieldcall is streamed, leading in an executionin order. But with the string version above, the order of execution is different:

  • theyield call requires an argument, astring

  • that arguments need to be evaluatedbefore theyield call is generated

so evaluating the string leads to an execution of thea(href:…​) callbeforeyield is itself called. This is notwhat you want to do. Instead, you want to generate astring which contains markup, which is then passed to theyieldcall. This can be done this way:

p("This is a ${stringOf {a(href:'target.html', "link")}} to another page")

Note thestringOf call, which basically tells the markup template engine that the underlying markup needs to be renderedseparately and exported as a string. Note that for simple expressions,stringOf can be replaced by an alternate tagnotation that starts with adollar sign:

p("This is a ${$a(href:'target.html', "link")} to another page")
It is worth noting that usingstringOf or the special$tag notation triggers the creation of a distinct string writerwhich is then used to render the markup. It is slower than using the version with calls toyield which perform directstreaming of the markup instead.
Internationalization

The template engine has native support for internationalization. For that, when you create theTemplateConfiguration, you can provideaLocale which is the default locale to be used for templates. Each template may have different versions, one for each locale. Thename of the template makes the difference:

  • file.tpl: default template file

  • file_fr_FR.tpl: french version of the template

  • file_en_US.tpl: american english version of the template

  • …​

When a template is rendered or included, then:

  • if the template name or include nameexplicitly sets a locale, thespecific version is included, or the default version if not found

  • if the template name doesn’t include a locale, the version for theTemplateConfiguration locale is used, or the default version if not found

For example, imagine the default locale is set toLocale.ENGLISH and that the main template includes:

Use an explicit locale in include
include template: 'locale_include_fr_FR.tpl'

then the template is rendered using the specific template:

Bypass the template configuration
Texte en français

Using an include without specifying a locale will make the template engine look for a template with the configured locale, and if not, fallback to the default, like here:

Don’t use a locale in include
include template: 'locale_include.tpl'
Fallback to the default template
Default text

However, changing the default locale of the template engine toLocale.FRANCE will change the output, because the template engine will now look for a filewith thefr_FR locale:

Don’t fall back to the default template because a locale specific template was found
Texte en français

This strategy lets you translate your templates one by one, by relying on default templates, for which no locale is set in the file name.

Custom template classes

By default, templates created inherit thegroovy.text.markup.BaseTemplate class. It may be interesting for an application to provide a differenttemplate class, for example to provide additional helper methods which are aware of the application, or customized rendering primitives (for HTML,for example).

The template engine provides this ability by setting an alternativebaseTemplateClass in theTemplateConfiguration:

config.setBaseTemplateClass(MyTemplate.class);

The custom base class has to extendBaseClass like in this example:

public abstract class MyTemplate extends BaseTemplate {    private List<Module> modules    public MyTemplate(            final MarkupTemplateEngine templateEngine,            final Map model,            final Map<String, String> modelTypes,            final TemplateConfiguration configuration) {        super(templateEngine, model, modelTypes, configuration)    }    List<Module> getModules() {        return modules    }    void setModules(final List<Module> modules) {        this.modules = modules    }    boolean hasModule(String name) {        modules?.any { it.name == name }    }}

This example shows a class which provides an additional method namedhasModule, which can then be used directly in the template:

if (hasModule('foo')) {    p 'Found module [foo]'} else {    p 'Module [foo] not found'}
Type checked templates
Optional type checking

Even if templates are not type checked, they are statically compiled. This means that once the templates are compiled, performance should be very good. For someapplications, it might be good to make sure that templates are valid before they are actually rendered. This means failing template compilation, for example, ifa method on a model variable doesn’t exist.

TheMarkupTemplateEngine provides such a facility. Templates can be optionally type checked. For that, the developer must provide additional information attemplate creation time, which is the types of the variables found in the model. Imagine a model exposing a list of pages, where a page is defined as:

Page.groovy
public class Page {    Long id    String title    String body}

Then a list of pages can be exposed in the model, like this:

Page p = new Page();p.setTitle("Sample page");p.setBody("Page body");List<Page> pages = new LinkedList<>();pages.add(p);model = new HashMap<String,Object>();model.put("pages", pages);

A template can use it easily:

pages.each { page ->(1)    p("Page title: $page.title")(2)    p(page.text)(3)}
1iterate on pages from the model
2page.title is valid
3page.text isnot (should bepage.body)

Without type checking, the compilation of the template succeeds, because the template engine doesn’t know about the model until a pageis actually rendered. This means that the problem would only surface at runtime, once the page is rendered:

Runtime error
No such property: text

In some situations, this can be complicated to sort out or even notice. By declaring the type of thepages to the template engine, we’re now capable of failing at compile time:

modelTypes = new HashMap<String,String>();(1)modelTypes.put("pages", "List<Page>");(2)Template template = engine.createTypeCheckedModelTemplate("main.tpl", modelTypes)(3)
1create a map which will hold the model types
2declare the type of thepages variables (note the use of a string for the type)
3usecreateTypeCheckedModelTemplate instead ofcreateTemplate

This time, when the template is compiled at the last line, an error occurs:

Template compilation time error
[Static type checking] - No such property: text for class: Page

This means that you don’t need to wait for the page to be rendered to see an error. The use ofcreateTypeCheckedModelTemplate is mandatory.

Alternative declaration of types

Alternatively, if the developer is also the one who writes the templates, it is possible to declare the types of the expected variablesdirectly in the template. In this case, even if you callcreateTemplate, it will be type checked:

Inline declaration of types
modelTypes = {(1)    List<Page> pages(2)}pages.each { page ->    p("Page title: $page.title")    p(page.text)}
1types need to be declared in themodelTypes header
2declare one variable per object in the model
Performance of type checked templates

An additional interest of using type checked models is that performance should improve. By telling the type checker what are the expected types,you also let the compiler generate optimized code for that, so if you are looking for the best performance, consider using type checked templates.

3.18.8. Other solutions

Also, there are other templating solutions that can be used along with Groovy,such asFreeMarker,Velocity,StringTemplate and others.

3.19. Servlet support

You can write (Java) Servlets in Groovy (called Groovlets).

There is also aGroovyServlet.

This feature will automatically compile your .groovy source files, turn them into bytecode, load the Class and cache it until you change the source file.

Here’s a simple example to show you the kind of things you can do from a Groovlet.

Notice the use of implicit variables to access the session, output and request. Also notice that this is more like a script as it does not have a class wrapper.

if (!session) {  session = request.getSession(true)}if (!session.counter) {  session.counter = 1}println """<html>    <head>        <title>Groovy Servlet</title>    </head>    <body>        <p>Hello, ${request.remoteHost}: ${session.counter}! ${new Date()}        </p>    </body></html>"""session.counter = session.counter + 1

Or, do the same thing using MarkupBuilder:

if (!session) {    session = request.getSession(true)}if (!session.counter) {    session.counter = 1}html.html { // html is implicitly bound to new MarkupBuilder(out)  head {      title('Groovy Servlet')  }  body {    p("Hello, ${request.remoteHost}: ${session.counter}! ${new Date()}")  }}session.counter = session.counter + 1

3.19.1. Implicit variables

The following variables are ready for use in Groovlets:

variable namebound tonote

request

ServletRequest

-

response

ServletResponse

-

context

ServletContext

-

application

ServletContext

-

session

getSession(false)

can be null! see <1>

params

a Map object

headers

a Map object

out

response.getWriter()

see <2>

sout

response.getOutputStream()

see <2>

html

new MarkupBuilder(out)

see <2>

json

new StreamingJsonBuilder(out)

see <2>

  1. The session variable is only set, if there was already a session object. See theif (session == null) checks in the examples above.

  2. These variables cannot be re-assigned inside aGroovlet. They are bound on first access, allowing to e.g. calling methods on theresponse object before usingout.

3.19.2. Setting up groovlets

Add the following to yourweb.xml:

<servlet>    <servlet-name>Groovy</servlet-name>    <servlet-class>groovy.servlet.GroovyServlet</servlet-class></servlet><servlet-mapping>    <servlet-name>Groovy</servlet-name>    <url-pattern>*.groovy</url-pattern></servlet-mapping>

Then put the required groovy jar files intoWEB-INF/lib.

Now put the .groovy files in, say, the root directory (i.e. where you would put your html files). TheGroovyServlet takes care of compiling the .groovy files.

So for example using tomcat you could edittomcat/conf/server.xml like this:

<Context path="/groovy" docBase="c:/groovy-servlet"/>

3.20. Integrating Groovy in a Java application

3.20.1. Groovy integration mechanisms

The Groovy language proposes several ways to integrate itself into applications (Java or even Groovy) at runtime, fromthe most basic, simple code execution to the most complete, integrating caching and compiler customization.

All the examples written in this section are using Groovy, but the same integration mechanisms can be used fromJava.
Eval

Thegroovy.util.Eval class is the simplest way to execute Groovy dynamically at runtime. This can be done by calling thememethod:

import groovy.util.Evalassert Eval.me('33*3') == 99assert Eval.me('"foo".toUpperCase()') == 'FOO'

Eval supports multiple variants that accept parameters for simple evaluation:

assert Eval.x(4, '2*x') == 8(1)assert Eval.me('k', 4, '2*k') == 8(2)assert Eval.xy(4, 5, 'x*y') == 20(3)assert Eval.xyz(4, 5, 6, 'x*y+z') == 26(4)
1Simple evaluation with one bound parameter namedx
2Same evaluation, with a custom bound parameter namedk
3Simple evaluation with two bound parameters namedx andy
4Simple evaluation with three bound parameters namedx,y andz

TheEval class makes it very easy to evaluate simple scripts, but doesn’t scale: there is no caching of the script, andit isn’t meant to evaluate more than one-liners.

GroovyShell
Multiple sources

Thegroovy.lang.GroovyShell class is the preferred way to evaluate scripts with the ability to cache the resultingscript instance. Although theEval class returns the result of the execution of the compiled script, theGroovyShellclass offers more options.

def shell = new GroovyShell()(1)def result = shell.evaluate '3*5'(2)def result2 = shell.evaluate(new StringReader('3*5'))(3)assert result == result2def script = shell.parse '3*5'(4)assert script instanceof groovy.lang.Scriptassert script.run() == 15(5)
1create a newGroovyShell instance
2can be used asEval with direct execution of the code
3can read from multiple sources (String,Reader,File,InputStream)
4can defer execution of the script.parse returns aScript instance
5Script defines arun method
Sharing data between a script and the application

It is possible to share data between the application and the script using agroovy.lang.Binding:

def sharedData = new Binding()(1)def shell = new GroovyShell(sharedData)(2)def now = new Date()sharedData.setProperty('text', 'I am shared data!')(3)sharedData.setProperty('date', now)(4)String result = shell.evaluate('"At $date, $text"')(5)assert result == "At $now, I am shared data!"
1create a newBinding that will contain shared data
2create aGroovyShell using this shared data
3add a string to the binding
4add a date to the binding (you are not limited to simple types)
5evaluate the script

Note that it is also possible to write from the script into the binding:

def sharedData = new Binding()(1)def shell = new GroovyShell(sharedData)(2)shell.evaluate('foo=123')(3)assert sharedData.getProperty('foo') == 123(4)
1create a newBinding instance
2create a newGroovyShell using that shared data
3use anundeclared variable to store the result into the binding
4read the result from the caller

It is important to understand that you need to use an undeclared variable if you want to write into the binding. Usingdef or anexplicit type like in the example below would fail because you would then create alocal variable:

def sharedData = new Binding()def shell = new GroovyShell(sharedData)shell.evaluate('int foo=123')try {    assert sharedData.getProperty('foo')} catch (MissingPropertyException e) {    println "foo is defined as a local variable"}
You must be very careful when using shared data in a multithreaded environment. TheBinding instance thatyou pass toGroovyShell isnot thread safe, and shared by all scripts.

It is possible to work around the shared instance ofBinding by leveraging theScript instance which is returnedbyparse:

def shell = new GroovyShell()def b1 = new Binding(x:3)(1)def b2 = new Binding(x:4)(2)def script = shell.parse('x = 2*x')script.binding = b1script.run()script.binding = b2script.run()assert b1.getProperty('x') == 6assert b2.getProperty('x') == 8assert b1 != b2
1will store thex variable insideb1
2will store thex variable insideb2

However, you must be aware that you are still sharing thesame instance of a script. So this technique cannot beused if you have two threads working on the same script. In that case, you must make sure of creating two distinctscript instances:

def shell = new GroovyShell()def b1 = new Binding(x:3)def b2 = new Binding(x:4)def script1 = shell.parse('x = 2*x')(1)def script2 = shell.parse('x = 2*x')(2)assert script1 != script2script1.binding = b1(3)script2.binding = b2(4)def t1 = Thread.start { script1.run() }(5)def t2 = Thread.start { script2.run() }(6)[t1,t2]*.join()(7)assert b1.getProperty('x') == 6assert b2.getProperty('x') == 8assert b1 != b2
1create an instance of script for thread 1
2create an instance of script for thread 2
3assign first binding to script 1
4assign second binding to script 2
5start first script in a separate thread
6start second script in a separate thread
7wait for completion

In case you need thread safety like here, it is more advisable to use theGroovyClassLoaderdirectly instead.

Custom script class

We have seen that theparse method returns an instance ofgroovy.lang.Script, but it is possible to use a customclass, given that it extendsScript itself. It can be used to provide additional behavior to the script like inthe example below:

abstract class MyScript extends Script {    String name    String greet() {        "Hello, $name!"    }}

The custom class defines a property calledname and a new method calledgreet. This class can be used as the scriptbase class by using a custom configuration:

import org.codehaus.groovy.control.CompilerConfigurationdef config = new CompilerConfiguration()(1)config.scriptBaseClass = 'MyScript'(2)def shell = new GroovyShell(this.class.classLoader, new Binding(), config)(3)def script = shell.parse('greet()')(4)assert script instanceof MyScriptscript.setName('Michel')assert script.run() == 'Hello, Michel!'
1create aCompilerConfiguration instance
2instruct it to useMyScript as the base class for scripts
3then use the compiler configuration when you create the shell
4the script now has access to the new methodgreet
You are not limited to the solescriptBaseClass configuration. You can use any of the compiler configurationtweaks, including thecompilation customizers.
GroovyClassLoader

In theprevious section, we have shown thatGroovyShell was an easy tool to execute scripts, butit makes it complicated to compile anything but scripts. Internally, it makes use of thegroovy.lang.GroovyClassLoader,which is at the heart of the compilation and loading of classes at runtime.

By leveraging theGroovyClassLoader instead ofGroovyShell, you will be able to load classes, instead of instancesof scripts:

import groovy.lang.GroovyClassLoaderdef gcl = new GroovyClassLoader()(1)def clazz = gcl.parseClass('class Foo { void doIt() { println "ok" } }')(2)assert clazz.name == 'Foo'(3)def o = clazz.newInstance()(4)o.doIt()(5)
1create a newGroovyClassLoader
2parseClass will return an instance ofClass
3you can check that the class which is returns is really the one defined in the script
4and you can create a new instance of the class, which is not a script
5then call any method on it
A GroovyClassLoader keeps a reference of all the classes it created, so it is easy to create a memory leak. Inparticular, if you execute the same script twice, if it is a String, then you obtain two distinct classes!
import groovy.lang.GroovyClassLoaderdef gcl = new GroovyClassLoader()def clazz1 = gcl.parseClass('class Foo { }')(1)def clazz2 = gcl.parseClass('class Foo { }')(2)assert clazz1.name == 'Foo'(3)assert clazz2.name == 'Foo'assert clazz1 != clazz2(4)
1dynamically create a class named "Foo"
2create an identical looking class, using a separateparseClass call
3make sure both classes have the same name
4but they are actually different!

The reason is that aGroovyClassLoader doesn’t keep track of the source text. If you want to have the same instance,then the sourcemust be a file, like in this example:

def gcl = new GroovyClassLoader()def clazz1 = gcl.parseClass(file)(1)def clazz2 = gcl.parseClass(new File(file.absolutePath))(2)assert clazz1.name == 'Foo'(3)assert clazz2.name == 'Foo'assert clazz1 == clazz2(4)
1parse a class from aFile
2parse a class from a distinct file instance, but pointing to the same physical file
3make sure our classes have the same name
4but now, they are the same instance

Using aFile as input, theGroovyClassLoader is capable ofcaching the generated class file, which avoidscreating multiple classes at runtime for the same source.

GroovyScriptEngine

Thegroovy.util.GroovyScriptEngine class provides a flexible foundation for applications which rely on scriptreloading and script dependencies. WhileGroovyShell focuses on standaloneScripts andGroovyClassLoader handlesdynamic compilation and loading of any Groovy class, theGroovyScriptEngine will add a layer on top ofGroovyClassLoaderto handle both script dependencies and reloading.

To illustrate this, we will create a script engine and execute code in an infinite loop. First of all, you need to createa directory with the following script inside:

ReloadingTest.groovy
class Greeter {    String sayHello() {        def greet = "Hello, world!"        greet    }}new Greeter()

then you can execute this code using aGroovyScriptEngine:

def binding = new Binding()def engine = new GroovyScriptEngine([tmpDir.toURI().toURL()] as URL[])(1)while (true) {    def greeter = engine.run('ReloadingTest.groovy', binding)(2)    println greeter.sayHello()(3)    Thread.sleep(1000)}
1create a script engine which will look for sources into our source directory
2execute the script, which will return an instance ofGreeter
3print the greeting message

At this point, you should see a message printed every second:

Hello, world!Hello, world!...

Without interrupting the script execution, now replace the contents of theReloadingTest file with:

ReloadingTest.groovy
class Greeter {    String sayHello() {        def greet = "Hello, Groovy!"        greet    }}new Greeter()

And the message should change to:

Hello, world!...Hello, Groovy!Hello, Groovy!...

But it is also possible to have a dependency on another script. To illustrate this, create the following file intothe same directory, without interrupting the executing script:

Dependency.groovy
class Dependency {    String message = 'Hello, dependency 1'}

and update theReloadingTest script like this:

ReloadingTest.groovy
import Dependencyclass Greeter {    String sayHello() {        def greet = new Dependency().message        greet    }}new Greeter()

And this time, the message should change to:

Hello, Groovy!...Hello, dependency 1!Hello, dependency 1!...

And as a last test, you can update theDependency.groovy file without touching theReloadingTest file:

Dependency.groovy
class Dependency {    String message = 'Hello, dependency 2'}

And you should observe that the dependent file was reloaded:

Hello, dependency 1!...Hello, dependency 2!Hello, dependency 2!
CompilationUnit

Ultimately, it is possible to perform more operations during compilation by relying directly on theorg.codehaus.groovy.control.CompilationUnit class. This class is responsible for determining the various steps ofcompilation and would let you introduce new steps or even stop compilation at various phases. This is for example howstub generation is done, for the joint compiler.

However, overridingCompilationUnit is not recommended and should only be done if no other standard solution works.

3.20.2. JSR 223 javax.script API

JSR-223 is a standard API for calling scripting frameworks in Java. It is available since Java 6 and aims atproviding a common framework for calling multiple languages from Java. Groovy provides its own richer integration mechanisms,and if you don’t plan to use multiple languages in the same application, it is recommended that you use the Groovyintegration mechanisms instead of the limited JSR-223 API.

Here is how you need to initialize the JSR-223 engine to talk to Groovy from Java:

import javax.script.ScriptEngine;import javax.script.ScriptEngineManager;import javax.script.ScriptException;...ScriptEngineManager factory = new ScriptEngineManager();ScriptEngine engine = factory.getEngineByName("groovy");

Then you can execute Groovy scripts easily:

Integer sum = (Integer) engine.eval("(1..10).sum()");assertEquals(Integer.valueOf(55), sum);

It is also possible to share variables:

engine.put("first", "HELLO");engine.put("second", "world");String result = (String) engine.eval("first.toLowerCase() + ' ' + second.toUpperCase()");assertEquals("hello WORLD", result);

This next example illustrates calling an invokable function:

import javax.script.Invocable;...ScriptEngineManager factory = new ScriptEngineManager();ScriptEngine engine = factory.getEngineByName("groovy");String fact = "def factorial(n) { n == 1 ? 1 : n * factorial(n - 1) }";engine.eval(fact);Invocable inv = (Invocable) engine;Object[] params = {5};Object result = inv.invokeFunction("factorial", params);assertEquals(Integer.valueOf(120), result);

The engine keeps per default hard references to the script functions. Tochange this you should set an engine level scoped attribute to the scriptcontext of the name#jsr223.groovy.engine.keep.globals with aString beingphantom to use phantom references,weak to use weakreferences orsoft to use soft references - casing is ignored. Anyother string will cause the use of hard references.

3.21. Domain-Specific Languages

3.21.1. Command chains

Groovy lets you omit parentheses around the arguments of amethod call for top-level statements. "command chain" feature extends this by allowing us to chain suchparentheses-free method calls, requiring neither parentheses around arguments, nor dots between the chained calls.The general idea is that a call likea b c d will actually be equivalent toa(b).c(d). Thisalso works with multiple arguments, closure arguments, and even named arguments. Furthermore, such command chains canalso appear on the right-hand side of assignments. Let’s have a look at some examplessupported by this new syntax:

// equivalent to: turn(left).then(right)turn left then right// equivalent to: take(2.pills).of(chloroquinine).after(6.hours)take 2.pills of chloroquinine after 6.hours// equivalent to: paint(wall).with(red, green).and(yellow)paint wall with red, green and yellow// with named parameters too// equivalent to: check(that: margarita).tastes(good)check that: margarita tastes good// with closures as parameters// equivalent to: given({}).when({}).then({})given { } when { } then { }

It is also possible to use methods in the chain which take no arguments,but in that case, the parentheses are needed:

// equivalent to: select(all).unique().from(names)select all unique() from names

If your command chain contains an odd number of elements, the chain willbe composed of method / arguments, and will finish by a final propertyaccess:

// equivalent to: take(3).cookies// and also this: take(3).getCookies()take 3 cookies

This command chain approach opens up interesting possibilities in terms of the much wider range of DSLs whichcan now be written in Groovy.

The above examples illustrate using a command chain based DSL but not how to create one. There are various strategiesthat you can use, but to illustrate creating such a DSL, we will show a couple of examples - first using maps and Closures:

show = { println it }square_root = { Math.sqrt(it) }def please(action) {  [the: { what ->    [of: { n -> action(what(n)) }]  }]}// equivalent to: please(show).the(square_root).of(100)please show the square_root of 100// ==> 10.0

As a second example, consider how you might write a DSL for simplifyingone of your existing APIs. Maybe you need to put this code in front ofcustomers, business analysts or testers who might be not hard-core Javadevelopers. We’ll use theSplitter from the GoogleGuava libraries project as italready has a nice Fluent API. Here is how we might use it out of thebox:

@Grab('com.google.guava:guava:r09')import com.google.common.base.*def result = Splitter.on(',').trimResults(CharMatcher.is('_' as char)).split("_a ,_b_ ,c__").iterator().toList()

It reads fairly well for a Java developer but if that is not your targetaudience or you have many such statements to write, it could beconsidered a little verbose. Again, there are many options for writing aDSL. We’ll keep it simple with Maps and Closures. We’ll first write ahelper method:

@Grab('com.google.guava:guava:r09')import com.google.common.base.*def split(string) {  [on: { sep ->    [trimming: { trimChar ->      Splitter.on(sep).trimResults(CharMatcher.is(trimChar as char)).split(string).iterator().toList()    }]  }]}

now instead of this line from our original example:

def result = Splitter.on(',').trimResults(CharMatcher.is('_' as char)).split("_a ,_b_ ,c__").iterator().toList()

we can write this:

def result = split "_a ,_b_ ,c__" on ',' trimming '_\'

3.21.2. Operator overloading

Various operators in Groovy are mapped onto regular method calls on objects.

This allows you to provide your own Java or Groovy objects which can take advantage of operator overloading. The following table describes the operators supported in Groovy and the methods they map to.

OperatorMethod

a + b

a.plus(b)

a - b

a.minus(b)

a * b

a.multiply(b)

a ** b

a.power(b)

a / b

a.div(b)

a % b

a.mod(b)

a | b

a.or(b)

a & b

a.and(b)

a ^ b

a.xor(b)

a++ or++a

a.next()

a-- or--a

a.previous()

a[b]

a.getAt(b)

a[b] = c

a.putAt(b, c)

a << b

a.leftShift(b)

a >> b

a.rightShift(b)

a >>> b

a.rightShiftUnsigned(b)

switch(a) { case(b) : }

b.isCase(a)

if(a)

a.asBoolean()

~a

a.bitwiseNegate()

-a

a.negative()

+a

a.positive()

a as b

a.asType(b)

a == b

a.equals(b)

a != b

! a.equals(b)

a <=> b

a.compareTo(b)

a > b

a.compareTo(b) > 0

a >= b

a.compareTo(b) >= 0

a \< b

a.compareTo(b) < 0

a <= b

a.compareTo(b) <= 0

3.21.3. Script base classes

The Script class

Groovy scripts are always compiled to classes. For example, a script as simple as:

println 'Hello from Groovy'

is compiled to a class extending the abstractgroovy.lang.Script class. This class contains a single abstractmethod calledrun. When a script is compiled, then its body will become therun method, while the other methodsfound in the script are found in the implementing class. TheScript class provides base support for integrationwith your application through theBinding object, as illustrated in this example:

def binding = new Binding()(1)def shell = new GroovyShell(binding)(2)binding.setVariable('x',1)(3)binding.setVariable('y',3)shell.evaluate 'z=2*x+y'(4)assert binding.getVariable('z') == 5(5)
1a binding is used to share data between the script and the calling class
2aGroovyShell can be used with this binding
3input variables are set from the calling class inside the binding
4then the script is evaluated
5and thez variable has been "exported" into the binding

This is a very practical way to share data between the caller and the script, however it may be insufficient or notpractical in some cases. For that purpose, Groovy allows you to set your own base script class. A base script classhas to extendgroovy.lang.Script and be a single abstract method type:

abstract class MyBaseClass extends Script {    String name    public void greet() { println "Hello, $name!" }}

Then the custom script base class can be declared in the compiler configuration, for example:

def config = new CompilerConfiguration()(1)config.scriptBaseClass = 'MyBaseClass'(2)def shell = new GroovyShell(this.class.classLoader, config)(3)shell.evaluate """    setName 'Judith'(4)    greet()"""
1create a custom compiler configuration
2set the base script class to our custom base script class
3then create aGroovyShell using that configuration
4the script will then extend the base script class, giving direct access to thename property andgreet method
The @BaseScript annotation

As an alternative, it is also possible to use the@BaseScript annotation directly into a script:

import groovy.transform.BaseScript@BaseScript MyBaseClass baseScriptsetName 'Judith'greet()

where@BaseScript should annotate a variable which type is the class of the base script. Alternatively, you can setthe base script class as a member of the@BaseScript annotation itself:

@BaseScript(MyBaseClass)import groovy.transform.BaseScriptsetName 'Judith'greet()
Alternate abstract method

We have seen that the base script class is a single abstract method type that needs to implement therun method. Therun method is executed by the script engine automatically. In some circumstances it may be interesting to have a baseclass which implements therun method, but provides an alternative abstract method to be used for the script body.For example, the base scriptrun method might perform some initialization before therun method is executed. Thisis possible by doing this:

abstract class MyBaseClass extends Script {    int count    abstract void scriptBody()(1)    def run() {        count++(2)        scriptBody()(3)        count(4)    }}
1the base script class should define one (and only one) abstract method
2therun method can be overridden and perform a task before executing the script body
3run calls the abstractscriptBody method which will delegate to the user script
4then it can return something else than the value from the script

If you execute this code:

def result = shell.evaluate """    println 'Ok'"""assert result == 1

Then you will see that the script is executed, but the result of the evaluation is1 as returned by therunmethod of the base class. It is even clearer if you useparse instead ofevaluate, because it would allow you toexecute therun method several times on the same script instance:

def script = shell.parse("println 'Ok'")assert script.run() == 1assert script.run() == 2

3.21.4. Adding properties to numbers

In Groovy number types are considered equal to any other types. As such, it is possible to enhance numbers by addingproperties or methods to them. This can be very handy when dealing with measurable quantities for example. Details abouthow existing classes can be enhanced in Groovy are found in theextensionmodules section or thecategories section.

An illustration of this can be found in Groovy using theTimeCategory:

use(TimeCategory)  {    println 1.minute.from.now(1)    println 10.hours.ago    def someDate = new Date()(2)    println someDate - 3.months}
1using theTimeCategory, a propertyminute is added to theInteger class
2similarly, themonths method returns agroovy.time.DatumDependentDuration which can be used in calculus

Categories are lexically bound, making them a great fit for internal DSLs.

3.21.5. @DelegatesTo

Explaining delegation strategy at compile time

@groovy.lang.DelegatesTo is a documentation and compile-time annotation aimed at:

  • documenting APIs that use closures as arguments

  • providing type information for the static type checker and compiler

The Groovy language is a platform of choice for building DSLs. Usingclosures, it’s quite easy to create custom control structures, as wellas it is simple to create builders. Imagine that you have the followingcode:

email {    from 'dsl-guru@mycompany.com'    to 'john.doe@waitaminute.com'    subject 'The pope has resigned!'    body {        p 'Really, the pope has resigned!'    }}

One way of implementing this is using the builder strategy, whichimplies a method, namedemail which accepts a closure as an argument.The method may delegate subsequent calls to an object that implementsthe fromtosubject and body methods. Again, body is amethod which accepts a closure as an argument and that uses the builderstrategy.

Implementing such a builder is usually done the following way:

def email(Closure cl) {    def email = new EmailSpec()    def code = cl.rehydrate(email, this, this)    code.resolveStrategy = Closure.DELEGATE_ONLY    code()}

theEmailSpec class implements the fromto, … methods. Bycalling rehydrate, we’re creating a copy of the closure for which weset the delegateowner and thisObject values. Setting the ownerand thethis object is not very important here since we will use theDELEGATE_ONLY strategy which says that the method calls will beresolved only against the delegate of the closure.

class EmailSpec {    void from(String from) { println "From: $from"}    void to(String... to) { println "To: $to"}    void subject(String subject) { println "Subject: $subject"}    void body(Closure body) {        def bodySpec = new BodySpec()        def code = body.rehydrate(bodySpec, this, this)        code.resolveStrategy = Closure.DELEGATE_ONLY        code()    }}

TheEmailSpec class has itself abody method accepting a closure that is cloned and executed. This is whatwe call the builder pattern in Groovy.

One of the problems with the code that we’ve shown is that the user oftheemail method doesn’t have any information about the methods thathe’s allowed to call inside the closure. The only possible informationis from the method documentation. There are two issues with this: firstof all, documentation is not always written, and if it is, it’s notalways available (javadoc not downloaded, for example). Second, itdoesn’t help IDEs. What would be really interesting, here, is for IDEsto help the developer by suggesting, once they are in the closure body,methods that exist on theemail class.

Moreover, if the user calls a method in the closure which is not definedby the EmailSpec class, the IDE should at least issue a warning (becauseit’s very likely that it will break at runtime).

One more problem with the code above is that it is not compatible with static type checking. Type checking would letthe user know if a method call is authorized at compile time instead of runtime, but if you try to perform typechecking on this code:

email {    from 'dsl-guru@mycompany.com'    to 'john.doe@waitaminute.com'    subject 'The pope has resigned!'    body {        p 'Really, the pope has resigned!'    }}

Then the type checker will know that there’s an email method acceptinga Closure, but it will complain about every method call inside theclosure, because from, for example, is not a method which is definedin the class. Indeed, it’s defined in the EmailSpec class and it hasabsolutely no hint to help it knowing that the closure delegate will, atruntime, be of type EmailSpec:

@groovy.transform.TypeCheckedvoid sendEmail() {    email {        from 'dsl-guru@mycompany.com'        to 'john.doe@waitaminute.com'        subject 'The pope has resigned!'        body {            p 'Really, the pope has resigned!'        }    }}

will fail compilation with errors like this one:

[Static type checking] - Cannot find matching method MyScript#from(java.lang.String). Please check if the declared type is correct and if the method exists. @ line 31, column 21.                       from 'dsl-guru@mycompany.com'
@DelegatesTo

For those reasons, Groovy 2.1 introduced a new annotationnamed @DelegatesTo. The goal of this annotation is to solve both thedocumentation issue, that will let your IDE know about the expectedmethods in the closure body, and it will also solve the type checkingissue, by giving hints to the compiler about what are the potentialreceivers of method calls in the closure body.

The idea is to annotate the Closure parameter of the email method:

def email(@DelegatesTo(EmailSpec) Closure cl) {    def email = new EmailSpec()    def code = cl.rehydrate(email, this, this)    code.resolveStrategy = Closure.DELEGATE_ONLY    code()}

What we’ve done here is telling the compiler (or the IDE) that when themethod will be called with a closure, the delegate of this closure willbe set to an object of type email. But there is still a problem: thedefault delegation strategy is not the one which is used in our method.So we will give more information and tell the compiler (or the IDE) thatthe delegation strategy is also changed:

def email(@DelegatesTo(strategy=Closure.DELEGATE_ONLY, value=EmailSpec) Closure cl) {    def email = new EmailSpec()    def code = cl.rehydrate(email, this, this)    code.resolveStrategy = Closure.DELEGATE_ONLY    code()}

Now, both the IDE and the type checker (if you are using@TypeChecked)will be aware of the delegate and the delegation strategy. This is verynice because it will both allow the IDE to provide smart completion, butit will also remove errors at compile time that exist only because thebehaviour of the program is normally only known at runtime!

The following code will now pass compilation:

@TypeCheckedvoid doEmail() {    email {        from 'dsl-guru@mycompany.com'        to 'john.doe@waitaminute.com'        subject 'The pope has resigned!'        body {            p 'Really, the pope has resigned!'        }    }}
DelegatesTo modes

@DelegatesTo supports multiple modes that we will describe with examplesin this section.

Simple delegation

In this mode, the only mandatory parameter is the value which says towhich class we delegate calls. Nothing more. We’re telling the compilerthat the type of the delegate will always be of the type documentedby @DelegatesTo (note that it can be a subclass, but if it is, themethods defined by the subclass will not be visible to the typechecker).

void body(@DelegatesTo(BodySpec) Closure cl) {    // ...}
Delegation strategy

In this mode, you must specify both the delegate class and adelegation strategy. This must be used if the closure will not be calledwith the default delegation strategy, which is Closure.OWNER_FIRST.

void body(@DelegatesTo(strategy=Closure.DELEGATE_ONLY, value=BodySpec) Closure cl) {    // ...}
Delegate to parameter

In this variant, we will tell the compiler that we are delegating toanother parameter of the method. Take the following code:

def exec(Object target, Closure code) {   def clone = code.rehydrate(target, this, this)   clone()}

Here, the delegate which will be used is not created inside the execmethod. In fact, we take an argument of the method and delegate to it.Usage may look like this:

def email = new Email()exec(email) {   from '...'   to '...'   send()}

Each of the method calls are delegated to the email parameter. This isa widely used pattern which is also supported by @DelegatesTo using acompanion annotation:

def exec(@DelegatesTo.Target Object target, @DelegatesTo Closure code) {   def clone = code.rehydrate(target, this, this)   clone()}

A closure is annotated with @DelegatesTo, but this time, withoutspecifying any class. Instead, we’re annotating another parameterwith @DelegatesTo.Target. The type of the delegate is then determinedat compile time. One could think that we are using the parameter type,which in this case is Object but this is not true. Take this code:

class Greeter {   void sayHello() { println 'Hello' }}def greeter = new Greeter()exec(greeter) {   sayHello()}

Remember that this works out of the box without having to annotatewith @DelegatesTo. However, to make the IDE aware of the delegatetype, or the type checker aware of it, we need to add @DelegatesTo.And in this case, it will know that the Greeter variable is oftype Greeter, so it will not report errors on the sayHellomethod even if the exec method doesn’t explicitly define the target asof type Greeter. This is a very powerful feature, because it preventsyou from writing multiple versions of the same exec method fordifferent receiver types!

In this mode, the @DelegatesTo annotation also supports the strategyparameter that we’ve described upper.

Multiple closures

In the previous example, the exec method accepted only one closure,but you may have methods that take multiple closures:

void fooBarBaz(Closure foo, Closure bar, Closure baz) {    ...}

Then nothing prevents you from annotating each closurewith @DelegatesTo:

class Foo { void foo(String msg) { println "Foo ${msg}!" } }class Bar { void bar(int x) { println "Bar ${x}!" } }class Baz { void baz(Date d) { println "Baz ${d}!" } }void fooBarBaz(@DelegatesTo(Foo) Closure foo, @DelegatesTo(Bar) Closure bar, @DelegatesTo(Baz) Closure baz) {   ...}

But more importantly, if you have multiple closures and multiplearguments, you can use several targets:

void fooBarBaz(    @DelegatesTo.Target('foo') foo,    @DelegatesTo.Target('bar') bar,    @DelegatesTo.Target('baz') baz,    @DelegatesTo(target='foo') Closure cl1,    @DelegatesTo(target='bar') Closure cl2,    @DelegatesTo(target='baz') Closure cl3) {    cl1.rehydrate(foo, this, this).call()    cl2.rehydrate(bar, this, this).call()    cl3.rehydrate(baz, this, this).call()}def a = new Foo()def b = new Bar()def c = new Baz()fooBarBaz(    a, b, c,    { foo('Hello') },    { bar(123) },    { baz(new Date()) })
At this point, you may wonder why we don’t use the parameter names asreferences. The reason is that the information (the parameter name) isnot always available (it’s a debug-only information), so it’s alimitation of the JVM.
Delegating to a generic type

In some situations, it is interesting to instruct the IDE or the compiler that the delegate type will not be a parameterbut a generic type. Imagine a configurator that runs on a list of elements:

public <T> void configure(List<T> elements, Closure configuration) {   elements.each { e->      def clone = configuration.rehydrate(e, this, this)      clone.resolveStrategy = Closure.DELEGATE_FIRST      clone.call()   }}

Then this method can be called with any list like this:

@groovy.transform.ToStringclass Realm {   String name}List<Realm> list = []3.times { list << new Realm() }configure(list) {   name = 'My Realm'}assert list.every { it.name == 'My Realm' }

To let the type checker and the IDE know that theconfigure method calls the closure on each element of the list, you need to use@DelegatesTo differently:

public <T> void configure(    @DelegatesTo.Target List<T> elements,    @DelegatesTo(strategy=Closure.DELEGATE_FIRST, genericTypeIndex=0) Closure configuration) {   def clone = configuration.rehydrate(e, this, this)   clone.resolveStrategy = Closure.DELEGATE_FIRST   clone.call()}

@DelegatesTo takes an optionalgenericTypeIndex argument that tells what is the index of the generic type that willbe used as the delegate type. Thismust be used in conjunction with@DelegatesTo.Target and the index starts at 0. Inthe example above, that means that the delegate type is resolved againstList<T>, and since the generic type at index0 isT and inferred as aRealm, the type checker infers that the delegate type will be of typeRealm.

We’re using agenericTypeIndex instead of a placeholder (T) because of JVM limitations.
Delegating to an arbitrary type

It is possible that none of the options above can represent the type you want to delegate to. For example, let’s definea mapper class which is parametrized with an object and defines a map method which returns an object of another type:

class Mapper<T,U> {(1)    final T value(2)    Mapper(T value) { this.value = value }    U map(Closure<U> producer) {(3)        producer.delegate = value        producer()    }}
1The mapper class takes two generic type arguments: the source type and the target type
2The source object is stored in a final field
3Themap method asks to convert the source object to a target object

As you can see, the method signature frommap does not give any information about what object willbe manipulated by the closure. Reading the method body, we know that it will be thevalue which isof typeT, butT is not found in the method signature, so we are facing a case where none of theavailable options for@DelegatesTo is suitable. For example, if we try to statically compile this code:

def mapper = new Mapper<String,Integer>('Hello')assert mapper.map { length() } == 5

Then the compiler will fail with:

Static type checking] - Cannot find matching method TestScript0#length()

In that case, you can use thetype member of the@DelegatesTo annotation to referenceT as a type token:

class Mapper<T,U> {    final T value    Mapper(T value) { this.value = value }    U map(@DelegatesTo(type="T") Closure<U> producer) {(1)        producer.delegate = value        producer()    }}
1The@DelegatesTo annotation references a generic type which is not found in the method signature

Note that you are not limited to generic type tokens. Thetype member can be used to represent complex types, suchasList<T> orMap<T,List<U>>. The reason why you should use that in last resort is that the type is only checkedwhen the type checker finds usage of@DelegatesTo, not when the annotated method itself is compiled. This means thattype safety is only ensured at the call site. Additionally, compilation will be slower (though probably unnoticeable formost cases).

3.21.6. Compilation customizers

Introduction

Whether you are using groovyc to compile classes or a GroovyShell,for example, to execute scripts, under the hood, acompiler configuration is used. This configuration holds informationlike the source encoding or the classpath but it can also be used to perform more operations like adding imports bydefault, applying AST transformations transparently or disabling global AST transformations.

The goal of compilation customizers is to make those common tasks easy to implement. For that, the CompilerConfigurationclass is the entry point. The general schema will always be based on the following code:

import org.codehaus.groovy.control.CompilerConfiguration// create a configurationdef config = new CompilerConfiguration()// tweak the configurationconfig.addCompilationCustomizers(...)// run your scriptdef shell = new GroovyShell(config)shell.evaluate(script)

Compilation customizers must extend the org.codehaus.groovy.control.customizers.CompilationCustomizer class. A customizer works:

  • on a specific compilation phase

  • on every class node being compiled

You can implement your own compilation customizer but Groovy includes some of the most common operations.

Import customizer

Using this compilation customizer, your code will have imports addedtransparently. This is in particular useful for scripts implementing aDSL where you want to avoid users from having to write imports. Theimport customizer will let you add all the variants of imports theGroovy language allows, that is:

  • class imports, optionally aliased

  • star imports

  • static imports, optionally aliased

  • static star imports

import org.codehaus.groovy.control.customizers.ImportCustomizerdef icz = new ImportCustomizer()// "normal" importicz.addImports('java.util.concurrent.atomic.AtomicInteger', 'java.util.concurrent.ConcurrentHashMap')// "aliases" importicz.addImport('CHM', 'java.util.concurrent.ConcurrentHashMap')// "static" importicz.addStaticImport('java.lang.Math', 'PI') // import static java.lang.Math.PI// "aliased static" importicz.addStaticImport('pi', 'java.lang.Math', 'PI') // import static java.lang.Math.PI as pi// "star" importicz.addStarImports 'java.util.concurrent' // import java.util.concurrent.*// "static star" importicz.addStaticStars 'java.lang.Math' // import static java.lang.Math.*

A detailed description of all shortcuts can be found inorg.codehaus.groovy.control.customizers.ImportCustomizer

AST transformation customizer

The AST transformation customizer is meant to apply AST transformationstransparently. Unlike global AST transformations that apply on everyclass being compiled as long as the transform is found on classpath(which has drawbacks like increasing the compilation time or sideeffects due to transformations applied where they should not), thecustomizer will allow you to selectively apply a transform only forspecific scripts or classes.

As an example, let’s say you want to be able to use @Log in a script.The problem is that @Log is normally applied on a class node and ascript, by definition, doesn’t require one. But implementation wise,scripts are classes, it’s just that you cannot annotate this implicitclass node with @Log. Using the AST customizer, you have a workaroundto do it:

import org.codehaus.groovy.control.customizers.ASTTransformationCustomizerimport groovy.util.logging.Logdef acz = new ASTTransformationCustomizer(Log)config.addCompilationCustomizers(acz)

That’s all! Internally, the @Log AST transformation is applied toevery class node in the compilation unit. This means that it will beapplied to the script, but also to classes defined within the script.

If the AST transformation that you are using accepts parameters, you canuse parameters in the constructor too:

def acz = new ASTTransformationCustomizer(Log, value: 'LOGGER')// use name 'LOGGER' instead of the default 'log'config.addCompilationCustomizers(acz)

As the AST transformation customizers works with objects instead of ASTnodes, not all values can be converted to AST transformation parameters.For example, primitive types are converted to ConstantExpression (thatisLOGGER is converted to new ConstantExpression('LOGGER'), but ifyour AST transformation takes a closure as an argument, then you have togive it a ClosureExpression, like in the following example:

def configuration = new CompilerConfiguration()def expression = new AstBuilder().buildFromCode(CompilePhase.CONVERSION) { -> true }.expression[0]def customizer = new ASTTransformationCustomizer(ConditionalInterrupt, value: expression, thrown: SecurityException)configuration.addCompilationCustomizers(customizer)def shell = new GroovyShell(configuration)shouldFail(SecurityException) {    shell.evaluate("""        // equivalent to adding @ConditionalInterrupt(value={true}, thrown: SecurityException)        class MyClass {            void doIt() { }        }        new MyClass().doIt()    """)}

For a complete list of options, please refer toorg.codehaus.groovy.control.customizers.ASTTransformationCustomizer

Secure AST customizer

This customizer will allow the developer of a DSL to restrict thegrammar of the language, for example, to prevent users from using particular constructs.It is only ``secure'' in that one aspect, i.e. limiting the allowable constructs within a DSL.It does not replace a security manager which might additionallybe needed as an orthogonal aspect of overall security.The only reason for it to exist is to limit the expressiveness of thelanguage. This customizer only works at the AST (abstract syntax tree)level, not at runtime! It can be strange at first glance, but it makesmuch more sense if you think of Groovy as a platform to build DSLs. Youmay not want a user to have a complete language at hand. In the examplebelow, we will demonstrate it using an example of language that onlyallows arithmetic operations, but this customizer allows you to:

  • allow/disallow creation of closures

  • allow/disallow imports

  • allow/disallow package definition

  • allow/disallow definition of methods

  • restrict the receivers of method calls

  • restrict the kind of AST expressions a user can use

  • restrict the tokens (grammar-wise) a user can use

  • restrict the types of the constants that can be used in code

For all those features, the secure AST customizer works using either anallowed list (list of elements that are permitted) or a disallowed list (list ofelements that are not permitted). For each type of feature (imports,tokens, …) you have the choice to use either an allowed or disallowed list,but you can mix dis/allowed lists for distinct features. Typically,you will choose allowed lists (which permits only the constructs listed and disallows all others).

import org.codehaus.groovy.control.customizers.SecureASTCustomizerimport static org.codehaus.groovy.syntax.Types.*(1)def scz = new SecureASTCustomizer()scz.with {    closuresAllowed = false // user will not be able to write closures    methodDefinitionAllowed = false // user will not be able to define methods    allowedImports = [] // empty allowed list means imports are disallowed    allowedStaticImports = [] // same for static imports    allowedStaticStarImports = ['java.lang.Math'] // only java.lang.Math is allowed    // the list of tokens the user can find    // constants are defined in org.codehaus.groovy.syntax.Types    allowedTokens = [(1)            PLUS,            MINUS,            MULTIPLY,            DIVIDE,            MOD,            POWER,            PLUS_PLUS,            MINUS_MINUS,            COMPARE_EQUAL,            COMPARE_NOT_EQUAL,            COMPARE_LESS_THAN,            COMPARE_LESS_THAN_EQUAL,            COMPARE_GREATER_THAN,            COMPARE_GREATER_THAN_EQUAL,    ].asImmutable()    // limit the types of constants that a user can define to number types only    allowedConstantTypesClasses = [(2)            Integer,            Float,            Long,            Double,            BigDecimal,            Integer.TYPE,            Long.TYPE,            Float.TYPE,            Double.TYPE    ].asImmutable()    // method calls are only allowed if the receiver is of one of those types    // be careful, it's not a runtime type!    allowedReceiversClasses = [(2)            Math,            Integer,            Float,            Double,            Long,            BigDecimal    ].asImmutable()}
1use for token types fromorg.codehaus.groovy.syntax.Types
2you can use class literals here

If what the secure AST customizer provides out of the box isn’t enoughfor your needs, before creating your own compilation customizer, youmight be interested in the expression and statement checkers that theAST customizer supports. Basically, it allows you to add custom checkson the AST tree, on expressions (expression checkers) or statements(statement checkers). For this, you mustimplement org.codehaus.groovy.control.customizers.SecureASTCustomizer.StatementCheckeror org.codehaus.groovy.control.customizers.SecureASTCustomizer.ExpressionChecker.

Those interfaces define a single method called isAuthorized, returninga boolean, and taking a Statement (or Expression) as a parameter. Itallows you to perform complex logic over expressions or statements totell if a user is allowed to do it or not.

For example, there’s no predefined configuration flag in the customizer whichwill let you prevent people from using an attribute expression. Using a customchecker, it is trivial:

def scz = new SecureASTCustomizer()def checker = { expr ->    !(expr instanceof AttributeExpression)} as SecureASTCustomizer.ExpressionCheckerscz.addExpressionCheckers(checker)

Then we can make sure that this works by evaluating a simple script:

new GroovyShell(config).evaluate '''    class A {        int val    }    def a = new A(val: 123)    a.@val(1)'''
1will fail compilation
Source aware customizer

This customizer may be used as a filter on other customizers. Thefilter, in that case, is the org.codehaus.groovy.control.SourceUnit.For this, the source aware customizer takes another customizer as adelegate, and it will apply customization of that delegate only and onlyif predicates on the source unit match.

SourceUnit gives you access to multiple things but in particular thefile being compiled (if compiling from a file, of course). It givesyou the potential to perform operation based on the file name, forexample. Here is how you would create a source aware customizer:

import org.codehaus.groovy.control.customizers.SourceAwareCustomizerimport org.codehaus.groovy.control.customizers.ImportCustomizerdef delegate = new ImportCustomizer()def sac = new SourceAwareCustomizer(delegate)

Then you can use predicates on the source aware customizer:

// the customizer will only be applied to classes contained in a file name ending with 'Bean'sac.baseNameValidator = { baseName ->    baseName.endsWith 'Bean'}// the customizer will only be applied to files which extension is '.spec'sac.extensionValidator = { ext -> ext == 'spec' }// source unit validation// allow compilation only if the file contains at most 1 classsac.sourceUnitValidator = { SourceUnit sourceUnit -> sourceUnit.AST.classes.size() == 1 }// class validation// the customizer will only be applied to classes ending with 'Bean'sac.classValidator = { ClassNode cn -> cn.endsWith('Bean') }
Customizer builder

If you are using compilation customizers in Groovy code (like theexamples above) then you can use an alternative syntax to customize compilation.A builder (org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder)simplifies the creation of customizers using a hierarchical DSL.

import org.codehaus.groovy.control.CompilerConfigurationimport static org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder.withConfig(1)def conf = new CompilerConfiguration()withConfig(conf) {    // ...(2)}
1static import of the builder method
2configuration goes here

The code sample above shows how to use the builder. A staticmethod, withConfig, takes a closure corresponding to the builder code,and automatically registers compilation customizers to theconfiguration. Every compilation customizer available in the distributioncan be configured this way:

Import customizer
withConfig(configuration) {   imports { // imports customizer      normal 'my.package.MyClass' // a normal import      alias 'AI', 'java.util.concurrent.atomic.AtomicInteger' // an aliased import      star 'java.util.concurrent' // star imports      staticMember 'java.lang.Math', 'PI' // static import      staticMember 'pi', 'java.lang.Math', 'PI' // aliased static import   }}
AST transformation customizer
withConfig(conf) {   ast(Log)(1)}withConfig(conf) {   ast(Log, value: 'LOGGER')(2)}
1apply @Log transparently
2apply @Log with a different name for the logger
Secure AST customizer
withConfig(conf) {   secureAst {       closuresAllowed = false       methodDefinitionAllowed = false   }}
Source aware customizer
withConfig(configuration){    source(extension: 'sgroovy') {        ast(CompileStatic)(1)    }}withConfig(configuration){    source(extensions: ['sgroovy','sg']) {        ast(CompileStatic)(2)    }}withConfig(configuration) {    source(extensionValidator: { it.name in ['sgroovy','sg']}) {        ast(CompileStatic)(2)    }}withConfig(configuration) {    source(basename: 'foo') {        ast(CompileStatic)(3)    }}withConfig(configuration) {    source(basenames: ['foo', 'bar']) {        ast(CompileStatic)(4)    }}withConfig(configuration) {    source(basenameValidator: { it in ['foo', 'bar'] }) {        ast(CompileStatic)(4)    }}withConfig(configuration) {    source(unitValidator: { unit -> !unit.AST.classes.any { it.name == 'Baz' } }) {        ast(CompileStatic)(5)    }}
1apply CompileStatic AST annotation on .sgroovy files
2apply CompileStatic AST annotation on .sgroovy or .sg files
3apply CompileStatic AST annotation on files whose name is 'foo'
4apply CompileStatic AST annotation on files whose name is 'foo' or 'bar'
5apply CompileStatic AST annotation on files that do not contain a class named 'Baz'
Inlining a customizer

Inlined customizer allows you to write a compilation customizerdirectly, without having to create a class for it.

withConfig(configuration) {    inline(phase:'CONVERSION') { source, context, classNode ->(1)        println "visiting $classNode"(2)    }}
1define an inlined customizer which will execute at the CONVERSION phase
2prints the name of the class node being compiled
Multiple customizers

Of course, the builder allows you to define multiple customizers atonce:

withConfig(configuration) {   ast(ToString)   ast(EqualsAndHashCode)}
Theconfigscript commandline parameter

So far, we have described how you can customize compilation usinga CompilationConfiguration class, but this is only possible if youembed Groovy and that you create your own instancesof CompilerConfiguration (then use it to create aGroovyShell,GroovyScriptEngine, …).

If you want it to be applied on the classes you compile with the normalGroovy compiler (that is to say withgroovyc,ant orgradle,for example), it is possible to use a commandline parameter namedconfigscriptthat takes a Groovy configuration script as argument.

This script gives you access to theCompilerConfiguration instance beforethe files are compiled (exposed into the configuration script as a variable namedconfiguration),so that you can tweak it.

It also transparently integrates the compiler configuration builder above. As an example, let’s seehow you would activate static compilation by default on all classes.

Configscript example: Static compilation by default

Normally, classes in Groovy are compiled with a dynamic runtime. You can activate static compilationby placing an annotation named@CompileStatic on any class. Some people would like to have thismode activated by default, that is to say not having to annotate (potentially many) classes.Usingconfigscript, makes this possible.First of all, you need to create a file namedconfig.groovy into saysrc/conf withthe following contents:

withConfig(configuration) {(1)   ast(groovy.transform.CompileStatic)}
1configuration references aCompilerConfiguration instance

That is actually all you need. You don’t have to import the builder, it’s automaticallyexposed in the script. Then, compile your files using the following command line:

groovyc -configscript src/conf/config.groovy src/main/groovy/MyClass.groovy

We strongly recommend you to separate configuration files from classes,hence why we suggest using thesrc/main andsrc/conf directories above.

Configscript example: Setting system properties

In a configuration script you can also set system properties, e.g.:

System.setProperty('spock.iKnowWhatImDoing.disableGroovyVersionCheck', 'true')

If you have numerous system properties to set, then using a configuration filewill reduce the need to set a bunch of system properties with a long command lineor appropriately defined environment variable.You can also share all the settings by simply sharing the config file.

AST transformations

If:

  • runtime metaprogramming doesn’t allow you to do what you want

  • you need to improve the performance of the execution of your DSLs

  • you want to leverage the same syntax as Groovy but with different semantics

  • you want to improve support for type checking in your DSLs

Then AST transformations are the way to go. Unlike the techniques used so far, AST transformations are meant tochange or generate code before it is compiled to bytecode. AST transformations are capable of adding new methods atcompile time for example, or totally changing the body of a method based on your needs. They are a very powerful toolbut also come at the price of not being easy to write. For more information about AST transformations, please takea look at thecompile-timemetaprogramming section of this manual.

3.21.7. Custom type checking extensions

It may be interesting, in some circumstances, to provide feedback about wrong code to the user as soon as possible,that is to say when the DSL script is compiled, rather than having to wait for the execution of the script. However,this is not often possible with dynamic code. Groovy actually provides a practical answer to this known astype checking extensions.

3.21.8. Builders

Many tasks require building things and the builder pattern is one techniqueused by developers to make building things easier, especially buildingof structures which are hierarchical in nature.This pattern is so ubiquitous that Groovy has special built-in support.Firstly, there are many built-in builders. Secondly, there areclasses which make it easier to write your own builders.

Existing builders

Groovy comes with many built-in builders. Let’s look at some of them.

SaxBuilder

A builder for generatingSimple API for XML (SAX) events.

If you have the following SAX handler:

class LogHandler extends org.xml.sax.helpers.DefaultHandler {    String log = ''    void startElement(String uri, String localName, String qName, org.xml.sax.Attributes attributes) {        log += "Start Element: $localName, "    }    void endElement(String uri, String localName, String qName) {        log += "End Element: $localName, "    }}

You can useSaxBuilder to generate SAX events for the handler like this:

def handler = new LogHandler()def builder = new groovy.xml.SAXBuilder(handler)builder.root() {    helloWorld()}

And then check that everything worked as expected:

assert handler.log == 'Start Element: root, Start Element: helloWorld, End Element: helloWorld, End Element: root, '
StaxBuilder

A Groovy builder that works withStreaming API for XML (StAX) processors.

Here is a simple example using the StAX implementation of Java to generate XML:

def factory = javax.xml.stream.XMLOutputFactory.newInstance()def writer = new StringWriter()def builder = new groovy.xml.StaxBuilder(factory.createXMLStreamWriter(writer))builder.root(attribute:1) {    elem1('hello')    elem2('world')}assert writer.toString() == '<?xml version="1.0" ?><root attribute="1"><elem1>hello</elem1><elem2>world</elem2></root>'

An external library such asJettison can be used as follows:

@Grab('org.codehaus.jettison:jettison:1.3.3')@GrabExclude('stax:stax-api') // part of Java 6 and laterimport org.codehaus.jettison.mapped.*def writer = new StringWriter()def mappedWriter = new MappedXMLStreamWriter(new MappedNamespaceConvention(), writer)def builder = new groovy.xml.StaxBuilder(mappedWriter)builder.root(attribute:1) {     elem1('hello')     elem2('world')}assert writer.toString() == '{"root":{"@attribute":"1","elem1":"hello","elem2":"world"}}'
DOMBuilder

A builder for parsing HTML, XHTML and XML into aW3C DOM tree.

For example this XMLString:

String recordsXML = '''    <records>      <car name='HSV Maloo' make='Holden' year='2006'>        <country>Australia</country>        <record type='speed'>Production Pickup Truck with speed of 271kph</record>      </car>      <car name='P50' make='Peel' year='1962'>        <country>Isle of Man</country>        <record type='size'>Smallest Street-Legal Car at 99cm wide and 59 kg in weight</record>      </car>      <car name='Royale' make='Bugatti' year='1931'>        <country>France</country>        <record type='price'>Most Valuable Car at $15 million</record>      </car>    </records>'''

Can be parsed into a DOM tree with aDOMBuilder like this:

def reader = new StringReader(recordsXML)def doc = groovy.xml.DOMBuilder.parse(reader)

And then processed further e.g. by usingDOMCategory:

def records = doc.documentElementuse(groovy.xml.dom.DOMCategory) {    assert records.car.size() == 3}
NodeBuilder

NodeBuilder is used for creating nested trees ofgroovy.util.Node objects for handling arbitrary data.To create a simple user list you use aNodeBuilder like this:

def nodeBuilder = new NodeBuilder()def userlist = nodeBuilder.userlist {    user(id: '1', firstname: 'John', lastname: 'Smith') {        address(type: 'home', street: '1 Main St.', city: 'Springfield', state: 'MA', zip: '12345')        address(type: 'work', street: '2 South St.', city: 'Boston', state: 'MA', zip: '98765')    }    user(id: '2', firstname: 'Alice', lastname: 'Doe')}

Now you can process the data further, e.g. by usingGPath expressions:

assert userlist.user.@firstname.join(', ') == 'John, Alice'assert userlist.user.find { it.@lastname == 'Smith' }.address.size() == 2
JsonBuilder

Groovy’sJsonBuilder makes it easy to create Json. For example to create this Json string:

String carRecords = '''    {        "records": {        "car": {            "name": "HSV Maloo",            "make": "Holden",            "year": 2006,            "country": "Australia",            "record": {              "type": "speed",              "description": "production pickup truck with speed of 271kph"            }          }      }    }'''

you can use aJsonBuilder like this:

JsonBuilder builder = new JsonBuilder()builder.records {  car {        name 'HSV Maloo'        make 'Holden'        year 2006        country 'Australia'        record {            type 'speed'            description 'production pickup truck with speed of 271kph'        }  }}String json = JsonOutput.prettyPrint(builder.toString())

We useJsonUnit to check that the builder produced the expected result:

JsonAssert.assertJsonEquals(json, carRecords)

If you need to customize the generated output you can pass aJsonGenerator instance when creating aJsonBuilder:

import groovy.json.*def generator = new JsonGenerator.Options()        .excludeNulls()        .excludeFieldsByName('make', 'country', 'record')        .excludeFieldsByType(Number)        .addConverter(URL) { url -> "http://groovy-lang.org" }        .build()JsonBuilder builder = new JsonBuilder(generator)builder.records {  car {        name 'HSV Maloo'        make 'Holden'        year 2006        country 'Australia'        homepage new URL('http://example.org')        record {            type 'speed'            description 'production pickup truck with speed of 271kph'        }  }}assert builder.toString() == '{"records":{"car":{"name":"HSV Maloo","homepage":"http://groovy-lang.org"}}}'
StreamingJsonBuilder

UnlikeJsonBuilder which creates a data structure in memory, which is handy in those situations where you want to alter the structure programmatically before output,StreamingJsonBuilder directly streams to a writer without any intermediate memory data structure.If you do not need to modify the structure and want a more memory-efficient approach, useStreamingJsonBuilder.

The usage ofStreamingJsonBuilder is similar toJsonBuilder. In order to create this Json string:

String carRecords = """    {      "records": {        "car": {          "name": "HSV Maloo",          "make": "Holden",          "year": 2006,          "country": "Australia",          "record": {            "type": "speed",            "description": "production pickup truck with speed of 271kph"          }        }      }    }"""

you use aStreamingJsonBuilder like this:

StringWriter writer = new StringWriter()StreamingJsonBuilder builder = new StreamingJsonBuilder(writer)builder.records {    car {        name 'HSV Maloo'        make 'Holden'        year 2006        country 'Australia'        record {            type 'speed'            description 'production pickup truck with speed of 271kph'        }    }}String json = JsonOutput.prettyPrint(writer.toString())

We useJsonUnit to check the expected result:

JsonAssert.assertJsonEquals(json, carRecords)

If you need to customize the generated output you can pass aJsonGenerator instance when creating aStreamingJsonBuilder:

def generator = new JsonGenerator.Options()        .excludeNulls()        .excludeFieldsByName('make', 'country', 'record')        .excludeFieldsByType(Number)        .addConverter(URL) { url -> "http://groovy-lang.org" }        .build()StringWriter writer = new StringWriter()StreamingJsonBuilder builder = new StreamingJsonBuilder(writer, generator)builder.records {    car {        name 'HSV Maloo'        make 'Holden'        year 2006        country 'Australia'        homepage new URL('http://example.org')        record {            type 'speed'            description 'production pickup truck with speed of 271kph'        }    }}assert writer.toString() == '{"records":{"car":{"name":"HSV Maloo","homepage":"http://groovy-lang.org"}}}'
SwingBuilder

SwingBuilder allows you to create full-fledged Swing GUIs in a declarative and concise fashion. It accomplishes this by employing a common idiom in Groovy, builders.Builders handle the busywork of creating complex objects for you, such as instantiating children, calling Swing methods, and attaching these children to their parents.As a consequence, your code is much more readable and maintainable, while still allowing you to access to the full range of Swing components.

Here’s a simple example of usingSwingBuilder:

import groovy.swing.SwingBuilderimport java.awt.BorderLayout as BLcount = 0new SwingBuilder().edt {  frame(title: 'Frame', size: [250, 75], show: true) {    borderLayout()    textlabel = label(text: 'Click the button!', constraints: BL.NORTH)    button(text:'Click Me',         actionPerformed: {count++; textlabel.text = "Clicked ${count} time(s)."; println "clicked"}, constraints:BL.SOUTH)  }}

Here is what it will look like:

SwingBuilder001

This hierarchy of components would normally be created through a series of repetitive instantiations, setters, and finally attaching this child to its respective parent.UsingSwingBuilder, however, allows you to define this hierarchy in its native form, which makes the interface design understandable simply by reading the code.

The flexibility shown here is made possible by leveraging the many programming features built-in to Groovy, such as closures, implicit constructor calling, import aliasing, and string interpolation.Of course, these do not have to be fully understood in order to useSwingBuilder; as you can see from the code above, their uses are intuitive.

Here is a slightly more involved example, with an example ofSwingBuilder code re-use via a closure.

import groovy.swing.SwingBuilderimport javax.swing.*import java.awt.*def swing = new SwingBuilder()def sharedPanel = {     swing.panel() {        label("Shared Panel")    }}count = 0swing.edt {    frame(title: 'Frame', defaultCloseOperation: JFrame.EXIT_ON_CLOSE, pack: true, show: true) {        vbox {            textlabel = label('Click the button!')            button(                text: 'Click Me',                actionPerformed: {                    count++                    textlabel.text = "Clicked ${count} time(s)."                    println "Clicked!"                }            )            widget(sharedPanel())            widget(sharedPanel())        }    }}

Here’s another variation that relies on observable beans and binding:

import groovy.swing.SwingBuilderimport groovy.beans.Bindableclass MyModel {   @Bindable int count = 0}def model = new MyModel()new SwingBuilder().edt {  frame(title: 'Java Frame', size: [100, 100], locationRelativeTo: null, show: true) {    gridLayout(cols: 1, rows: 2)    label(text: bind(source: model, sourceProperty: 'count', converter: { v ->  v? "Clicked $v times": ''}))    button('Click me!', actionPerformed: { model.count++ })  }}

@Bindable is one of the core AST Transformations. It generates all the required boilerplate code to turn a simple bean into an observable one. Thebind() node creates appropriatePropertyChangeListeners that will update the interested parties whenever aPropertyChangeEvent is fired.

AntBuilder
Here we describeAntBuilder which lets you write Ant build scriptsin Groovy rather than XML. You may also be interested in using Groovyfrom Ant using theGroovy Ant task.

Despite being primarily a build tool,Apache Ant isa very practical tool for manipulating files including zip files, copy, resource processing, and more.But if ever you’ve been working with abuild.xml file or someJelly scriptand found yourself a little restricted by all those pointy brackets, orfound it a bit weird using XML as a scripting language and wantedsomething a little cleaner and more straight forward, then maybe Antscripting with Groovy might be what you’re after.

Groovy has a helper class calledAntBuilder which makes the scriptingof Ant tasks really easy; allowing a real scripting language to be usedfor programming constructs (variables, methods, loops, logicalbranching, classes etc). It still looks like a neat concise version ofAnt’s XML without all those pointy brackets; though you can mix andmatch this markup inside your script. Ant itself is a collection of jarfiles. By adding them to your classpath, you can easily use them withinGroovy as is. We believe usingAntBuilder leads to more concise andreadily understood syntax.

AntBuilder exposes Ant tasks directly using the convenient builder notation thatwe are used to in Groovy. Here is the most basic example, which is printing a messageon the standard output:

def ant = new groovy.ant.AntBuilder()(1)ant.echo('hello from Ant!')(2)
1creates an instance ofAntBuilder
2executes theecho task with the message in parameter

Imagine that you need to create a ZIP file. It can be as simple as:

def ant = new AntBuilder()ant.zip(destfile: 'sources.zip', basedir: 'src')

In the next example, we demonstrate the use ofAntBuilder to copy a list of filesusing a classical Ant pattern directly in Groovy:

// let's just call one taskant.echo("hello")// here is an example of a block of Ant inside GroovyMarkupant.sequential {    echo("inside sequential")    def myDir = "build/AntTest/"    mkdir(dir: myDir)    copy(todir: myDir) {        fileset(dir: "src/test") {            include(name: "**/*.groovy")        }    }    echo("done")}// now let's do some normal Groovy againdef file = new File(ant.project.baseDir,"build/AntTest/some/pkg/MyTest.groovy")assert file.exists()

Another example would be iterating over a list of files matching a specific pattern:

// let's create a scanner of filesetsdef scanner = ant.fileScanner {    fileset(dir:"src/test") {        include(name:"**/My*.groovy")    }}// now let's iterate overdef found = falsefor (f in scanner) {    println("Found file $f")    found = true    assert f instanceof File    assert f.name.endsWith(".groovy")}assert found

Or executing a JUnit test:

ant.junit {    classpath { pathelement(path: '.') }    test(name:'some.pkg.MyTest')}

We can even go further by compiling and executing a Java file directly from Groovy:

ant.echo(file:'Temp.java', '''    class Temp {        public static void main(String[] args) {            System.out.println("Hello");        }    }''')ant.javac(srcdir:'.', includes:'Temp.java', fork:'true')ant.java(classpath:'.', classname:'Temp', fork:'true')ant.echo('Done')

It is worth mentioning thatAntBuilder is included inGradle, so you can use it in Gradlejust like you would in Groovy. Additional documentation can be found in theGradle manual.

CliBuilder

CliBuilder provides a compact way to specify the available options for a commandline application and thenautomatically parse the application’s commandline parameters according to that specification. By convention,a distinction is made betweenoption commandline parameters and any remaining parameters which are passedto an application as its arguments. Typically, several types of options might be supported such as-V or--tabsize=4.CliBuilder removes the burden of developing lots of code for commandline processing.Instead, it supports a somewhat declarative approach to declaring your options and then provides a single callto parse the commandline parameters with a simple mechanism to interrogate the options (you can think of thisas a simple model for your options).

Even though the details of each commandline you create could be quite different, the same main steps arefollowed each time. First, aCliBuilder instance is created. Then, allowed commandline options are defined.This can be done using adynamic api style or anannotation style.The commandline parameters are then parsed according to the options specification resulting in acollection of options which are then interrogated.

Here is a simple exampleGreeter.groovy script illustrating usage:

// import of CliBuilder not shown(1)// specify parametersdef cli = new CliBuilder(usage: 'groovy Greeter [option]')(2)cli.a(longOpt: 'audience', args: 1, 'greeting audience')(3)cli.h(longOpt: 'help', 'display usage')(4)// parse and process parametersdef options = cli.parse(args)(5)if (options.h) cli.usage()(6)else println "Hello ${options.a ? options.a : 'World'}"(7)
1Earlier versions of Groovy had a CliBuilder in thegroovy.util package and no import was necessary.In Groovy 2.5, this approach became deprecated: applications should instead choose thegroovy.cli.picocli orgroovy.cli.commons version.The groovy.util version in Groovy 2.5 points to the commons-cli version for backwards compatibility but has been removed in Groovy 3.0.
2define a newCliBuilder instance specifying an optional usage string
3specify a-a option taking a single argument with an optional long variant--audience
4specify a-h option taking no arguments with an optional long variant--help
5parse the commandline parameters supplied to the script
6if theh option is found display a usage message
7display a standard greeting or, if thea option is found, a customized greeting

Running this script with no commandline parameters, i.e.:

> groovy Greeter

results in the following output:

Hello World

Running this script with-h as the single commandline parameter, i.e.:

> groovy Greeter -h

results in the following output:

usage: groovy Greeter [option] -a,--audience <arg>   greeting audience -h,--help             display usage

Running this script with--audience Groovologist as the commandline parameters, i.e.:

> groovy Greeter --audience Groovologist

results in the following output:

Hello Groovologist

When creating theCliBuilder instance in the above example, we set the optionalusage propertywithin the constructor call. This follows Groovy’s normal ability to set additional propertiesof the instance during construction. There are numerous other properties which can be setsuch asheader andfooter. For the complete set of available properties, see theavailable properties for thegroovy.util.CliBuilder class.

When defining an allowed commandline option, both a short name (e.g. "h" for thehelp option shown previously)and a short description (e.g. "display usage" for thehelp option) must be supplied.In our example above, we also set someadditional properties such aslongOpt andargs. The following additionalproperties are supported when specifying an allowed commandline option:

NameDescriptionType

argName

the name of the argument for this option used in output

String

longOpt

the long representation or long name of the option

String

args

the number of argument values

int orString     (1)

optionalArg

whether the argument value is optional

boolean

required

whether the option is mandatory

boolean

type

the type of this option

Class

valueSeparator

the character that is the value separator

char     (2)

defaultValue

a default value

String

convert

converts the incoming String to the required type

Closure     (1)

(1) More details later
(2) Single character Strings are coerced to chars in special cases in Groovy

If you have an option with only alongOpt variant, you can use the special shortname of '_'to specify the option, e.g. :cli._(longOpt: 'verbose', 'enable verbose logging').Some of the remaining named parameters should be fairly self-explanatory while others deservea bit more explanation. But before further explanations, let’s look at ways of usingCliBuilder with annotations.

Using Annotations and an interface

Rather than making a series of method calls (albeit in a very declarative mini-DSL form)to specify the allowable options, you can provide an interface specification of the allowable options whereannotations are used to indicate and provide details for those options and for how unprocessedparameters are handled. Two annotations are used:groovy.cli.Option andgroovy.cli.Unparsed.

Here is how such a specification can be defined:

interface GreeterI {    @Option(shortName='h', description='display usage') Boolean help()(1)    @Option(shortName='a', description='greeting audience') String audience()(2)    @Unparsed(description = "positional parameters") List remaining()(3)}
1Specify a Boolean option set using-h or--help
2Specify a String option set using-a or--audience
3Specify where any remaining parameters will be stored

Note how the long name is automatically determined from the interface method name.You can use thelongName annotation attribute to override that behavior and specifya custom long name if you wish or use a longName of '_' to indicate that no long nameis to be provided. You will need to specify a shortName in such a case.

Here is how you could use the interface specification:

// import CliBuilder not showndef cli = new CliBuilder(usage: 'groovy Greeter')(1)def argz = '--audience Groovologist'.split()def options = cli.parseFromSpec(GreeterI, argz)(2)assert options.audience() == 'Groovologist'(3)argz = '-h Some Other Args'.split()options = cli.parseFromSpec(GreeterI, argz)(4)assert options.help()assert options.remaining() == ['Some', 'Other', 'Args'](5)
1Create aCliBuilder instance as before with optional properties
2Parse parameters using the interface specification
3Interrogate options using the methods from the interface
4Parse a different set of parameters
5Interrogate the remaining parameters

WhenparseFromSpec is called,CliBuilder automatically creates an instance implementing the interfaceand populates it. You simply call the interface methods to interrogate the option values.

Using Annotations and an instance

Alternatively, perhaps you already have a domain class containing the option information.You can simply annotate properties or setters from that class to enableCliBuilder to appropriatelypopulate your domain object. Each annotation both describes that option’s properties through the annotationattributes and indicates the setter theCliBuilder will use to populate that option in your domain object.

Here is how such a specification can be defined:

class GreeterC {    @Option(shortName='h', description='display usage')    Boolean help(1)    private String audience    @Option(shortName='a', description='greeting audience')    void setAudience(String audience) {(2)        this.audience = audience    }    String getAudience() { audience }    @Unparsed(description = "positional parameters")    List remaining(3)}
1Indicate that a Boolean property is an option
2Indicate that a String property (with explicit setter) is an option
3Specify where any remaining args will be stored

And here is how you could use the specification:

// import CliBuilder not showndef cli = new CliBuilder(usage: 'groovy Greeter [option]')(1)def options = new GreeterC()(2)def argz = '--audience Groovologist foo'.split()cli.parseFromInstance(options, argz)(3)assert options.audience == 'Groovologist'(4)assert options.remaining == ['foo'](5)
1Create aCliBuilder instance as before with optional parameters
2Create an instance forCliBuilder to populate
3Parse arguments populating the supplied instance
4Interrogate the String option property
5Interrogate the remaining arguments property

WhenparseFromInstance is called,CliBuilder automatically populates your instance.You simply interrogate the instance properties (or whatever accessor methods you have providedin your domain object) to access the option values.

Using Annotations and a script

Finally, there are two additional convenience annotation aliases specifically for scripts. Theysimply combine the previously mentioned annotations andgroovy.transform.Field.The groovydoc for those annotations reveals the details:groovy.cli.OptionField andgroovy.cli.UnparsedField.

Here is an example using those annotations in a self-contained script that would be calledwith the same arguments as shown for the instance example earlier:

// import CliBuilder not shownimport groovy.cli.OptionFieldimport groovy.cli.UnparsedField@OptionField String audience@OptionField Boolean help@UnparsedField List remainingnew CliBuilder().parseFromInstance(this, args)assert audience == 'Groovologist'assert remaining == ['foo']
Options with arguments

We saw in our initial example that some options act like flags, e.g.Greeter -h butothers take an argument, e.g.Greeter --audience Groovologist. The simplest casesinvolve options which act like flags or have a single (potentially optional) argument.Here is an example involving those cases:

// import CliBuilder not showndef cli = new CliBuilder()cli.a(args: 0, 'a arg')(1)cli.b(args: 1, 'b arg')(2)cli.c(args: 1, optionalArg: true, 'c arg')(3)def options = cli.parse('-a -b foo -c bar baz'.split())(4)assert options.a == trueassert options.b == 'foo'assert options.c == 'bar'assert options.arguments() == ['baz']options = cli.parse('-a -c -b foo bar baz'.split())(5)assert options.a == trueassert options.c == trueassert options.b == 'foo'assert options.arguments() == ['bar', 'baz']
1An option that is simply a flag - the default; setting args to 0 is allowed but not needed.
2An option with exactly one argument
3An option with an optional argument; it acts like a flag if the option is left out
4An example using this spec where an argument is supplied to the 'c' option
5An example using this spec where no argument is supplied to the 'c' option; it’s just a flag

Note: when an option with an optional argument is encountered, it will (somewhat) greedily consume thenext parameter from the supplied commandline parameters. If however, the next parameter matches a known long or shortoption (with leading single or double hyphens), that will take precedence, e.g.-b in the above example.

Option arguments may also be specified using the annotation style. Here is an interface option specificationillustrating such a definition:

interface WithArgsI {    @Option boolean a()    @Option String b()    @Option(optionalArg=true) String[] c()    @Unparsed List remaining()}

And here is how it is used:

def cli = new CliBuilder()def options = cli.parseFromSpec(WithArgsI, '-a -b foo -c bar baz'.split())assert options.a()assert options.b() == 'foo'assert options.c() == ['bar']assert options.remaining() == ['baz']options = cli.parseFromSpec(WithArgsI, '-a -c -b foo bar baz'.split())assert options.a()assert options.c() == []assert options.b() == 'foo'assert options.remaining() == ['bar', 'baz']

This example makes use of an array-typed option specification. We cover this in more detail shortly when we discussmultiple arguments.

Specifying a type

Arguments on the commandline are by nature Strings (or arguably can be considered Booleans for flags) but can beconverted to richer types automatically by supplying additional typing information. For theannotation-based argument definition style, these types are supplied using the field types for annotationproperties or return types of annotated methods (or the setter argument type for setter methods).For the dynamic method style of argument definition a special 'type' property is supportedwhich allows you to specify a Class name.

When an explicit type is defined, theargs named-parameter is assumed to be 1 (except for Boolean-typedoptions where it is 0 by default). An explicitargs parameter can still be provided if needed.Here is an example using types with the dynamic api argument definition style:

def argz = '''-a John -b -d 21 -e 1980 -f 3.5 -g 3.14159    -h cv.txt -i DOWN and some more'''.split()def cli = new CliBuilder()cli.a(type: String, 'a-arg')cli.b(type: boolean, 'b-arg')cli.c(type: Boolean, 'c-arg')cli.d(type: int, 'd-arg')cli.e(type: Long, 'e-arg')cli.f(type: Float, 'f-arg')cli.g(type: BigDecimal, 'g-arg')cli.h(type: File, 'h-arg')cli.i(type: RoundingMode, 'i-arg')def options = cli.parse(argz)assert options.a == 'John'assert options.bassert !options.cassert options.d == 21assert options.e == 1980Lassert options.f == 3.5fassert options.g == 3.14159assert options.h == new File('cv.txt')assert options.i == RoundingMode.DOWNassert options.arguments() == ['and', 'some', 'more']

Primitives, numeric types, files, enums and arrays thereof, are supported (they are converted usingorg.codehaus.groovy.runtime.StringGroovyMethods#asType).

Custom parsing of the argument String

If the supported types aren’t sufficient, you can supply a closure to handle the String to rich type conversionfor you. Here is a sample using the dynamic api style:

def argz = '''-a John -b Mary -d 2016-01-01 and some more'''.split()def cli = new CliBuilder()def lower = { it.toLowerCase() }cli.a(convert: lower, 'a-arg')cli.b(convert: { it.toUpperCase() }, 'b-arg')cli.d(convert: { Date.parse('yyyy-MM-dd', it) }, 'd-arg')def options = cli.parse(argz)assert options.a == 'john'assert options.b == 'MARY'assert options.d.format('dd-MM-yyyy') == '01-01-2016'assert options.arguments() == ['and', 'some', 'more']

Alternatively, you can use the annotation style by supplying the conversion closure as an annotation parameter.Here is an example specification:

interface WithConvertI {    @Option(convert={ it.toLowerCase() }) String a()    @Option(convert={ it.toUpperCase() }) String b()    @Option(convert={ Date.parse("yyyy-MM-dd", it) }) Date d()    @Unparsed List remaining()}

And an example using that specification:

Date newYears = Date.parse("yyyy-MM-dd", "2016-01-01")def argz = '''-a John -b Mary -d 2016-01-01 and some more'''.split()def cli = new CliBuilder()def options = cli.parseFromSpec(WithConvertI, argz)assert options.a() == 'john'assert options.b() == 'MARY'assert options.d() == newYearsassert options.remaining() == ['and', 'some', 'more']
Options with multiple arguments

Multiple arguments are also supported using anargs value greater than 1. There is a special named parameter,valueSeparator, which can also be optionally used when processing multiple arguments. It allows some additionalflexibility in the syntax supported when supplying such argument lists on the commandline. For example,supplying a value separator of ',' allows a comma-delimited list of values to be passed on the commandline.

Theargs value is normally an integer. It can be optionally supplied as a String. There are two specialString symbols:` and `\*`.The `*` value means 0 or more. The ` value means 1 or more.The* value is the same as using+ and also setting theoptionalArg value to true.

Accessing the multiple arguments follows a special convention. Simply add an 's' to the normal propertyyou would use to access the argument option and you will retrieve all the supplied arguments as a list.So, for a short option named 'a', you access the first 'a' argument usingoptions.a and the list ofall arguments usingoptions.as. It’s fine to have a shortname or longname ending in 's' so long as youdon’t also have the singular variant without the 's'. So, ifname is one of your options with multiple argumentsandguess is another with a single argument, there will be no confusion usingoptions.names andoptions.guess.

Here is an excerpt highlighting the use of multiple arguments:

// import CliBuilder not showndef cli = new CliBuilder()cli.a(args: 2, 'a-arg')cli.b(args: '2', valueSeparator: ',', 'b-arg')(1)cli.c(args: '+', valueSeparator: ',', 'c-arg')(2)def options = cli.parse('-a 1 2 3 4'.split())(3)assert options.a == '1'(4)assert options.as == ['1', '2'](5)assert options.arguments() == ['3', '4']options = cli.parse('-a1 -a2 3'.split())(6)assert options.as == ['1', '2']assert options.arguments() == ['3']options = cli.parse(['-b1,2'])(7)assert options.bs == ['1', '2']options = cli.parse(['-c', '1'])assert options.cs == ['1']options = cli.parse(['-c1'])assert options.cs == ['1']options = cli.parse(['-c1,2,3'])assert options.cs == ['1', '2', '3']
1Args value supplied as a String and comma value separator specified
2One or more arguments are allowed
3Two commandline parameters will be supplied as the 'b' option’s list of arguments
4Access the 'a' option’s first argument
5Access the 'a' option’s list of arguments
6An alternative syntax for specifying two arguments for the 'a' option
7The arguments to the 'b' option supplied as a comma-separated value

As an alternative to accessing multiple arguments using theplural name approach, you can use anarray-based type for the option. In this case, all options will always be returned via the arraywhich is accessed via the normal singular name. We’ll see an example of this next when discussingtypes.

Multiple arguments are also supported using the annotation style of option definition by using anarray type for the annotated class member (method or property) as this example shows:

interface ValSepI {    @Option(numberOfArguments=2) String[] a()    @Option(numberOfArgumentsString='2', valueSeparator=',') String[] b()    @Option(numberOfArgumentsString='+', valueSeparator=',') String[] c()    @Unparsed remaining()}

And used as follows:

def cli = new CliBuilder()def options = cli.parseFromSpec(ValSepI, '-a 1 2 3 4'.split())assert options.a() == ['1', '2']assert options.remaining() == ['3', '4']options = cli.parseFromSpec(ValSepI, '-a1 -a2 3'.split())assert options.a() == ['1', '2']assert options.remaining() == ['3']options = cli.parseFromSpec(ValSepI, ['-b1,2'] as String[])assert options.b() == ['1', '2']options = cli.parseFromSpec(ValSepI, ['-c', '1'] as String[])assert options.c() == ['1']options = cli.parseFromSpec(ValSepI, ['-c1'] as String[])assert options.c() == ['1']options = cli.parseFromSpec(ValSepI, ['-c1,2,3'] as String[])assert options.c() == ['1', '2', '3']
Types and multiple arguments

Here is an example using types and multiple arguments with the dynamic api argument definition style:

def argz = '''-j 3 4 5 -k1.5,2.5,3.5 and some more'''.split()def cli = new CliBuilder()cli.j(args: 3, type: int[], 'j-arg')cli.k(args: '+', valueSeparator: ',', type: BigDecimal[], 'k-arg')def options = cli.parse(argz)assert options.js == [3, 4, 5](1)assert options.j == [3, 4, 5](1)assert options.k == [1.5, 2.5, 3.5]assert options.arguments() == ['and', 'some', 'more']
1For an array type, the trailing 's' can be used but isn’t needed
Setting a default value

Groovy makes it easy using the Elvis operator to provide a default value at the point of usage of some variable,e.g.String x = someVariable ?: 'some default'. But sometimes you wish to make such a default part of theoptions specification to minimise the interrogators work in later stages.CliBuilder supports thedefaultValueproperty to cater for this scenario.

Here is how you could use it using the dynamic api style:

def cli = new CliBuilder()cli.f longOpt: 'from', type: String, args: 1, defaultValue: 'one', 'f option'cli.t longOpt: 'to', type: int, defaultValue: '35', 't option'def options = cli.parse('-f two'.split())assert options.hasOption('f')assert options.f == 'two'assert !options.hasOption('t')assert options.t == 35options = cli.parse('-t 45'.split())assert !options.hasOption('from')assert options.from == 'one'assert options.hasOption('to')assert options.to == 45

Similarly, you might want such a specification using the annotation style. Here is an example using an interfacespecification:

interface WithDefaultValueI {    @Option(shortName='f', defaultValue='one') String from()    @Option(shortName='t', defaultValue='35') int to()}

Which would be used like this:

def cli = new CliBuilder()def options = cli.parseFromSpec(WithDefaultValueI, '-f two'.split())assert options.from() == 'two'assert options.to() == 35options = cli.parseFromSpec(WithDefaultValueI, '-t 45'.split())assert options.from() == 'one'assert options.to() == 45

You can also use thedefaultValue annotation attribute when using annotations with an instance,though it’s probably just as easy to provide an initial value for the property (or backing field).

Use withTypeChecked

The dynamic api style of usingCliBuilder is inherently dynamic but you have a few optionsshould you want to make use of Groovy’s static type checking capabilities. Firstly, consider using theannotation style, for example, here is an interface option specification:

interface TypeCheckedI{    @Option String name()    @Option int age()    @Unparsed List remaining()}

And it can be used in combination with@TypeChecked as shown here:

@TypeCheckedvoid testTypeCheckedInterface() {    def argz = "--name John --age 21 and some more".split()    def cli = new CliBuilder()    def options = cli.parseFromSpec(TypeCheckedI, argz)    String n = options.name()    int a = options.age()    assert n == 'John' && a == 21    assert options.remaining() == ['and', 'some', 'more']}

Secondly, there is a feature of the dynamic api style which offers some support. The definition statementsare inherently dynamic but actually return a value which we have ignored in earlier examples.The returned value is in fact aTypedOption<Type> and specialgetAt support allows the optionsto be interrogated using the typed option, e.g.options[savedTypeOption]. So, if you have statementssimilar to these in a non type checked part of your code:

def cli = new CliBuilder()TypedOption<Integer> age = cli.a(longOpt: 'age', type: Integer, 'some age option')

Then, the following statements can be in a separate part of your code which is type checked:

def args = '--age 21'.split()def options = cli.parse(args)int a = options[age]assert a == 21

Finally, there is one additional convenience method offered byCliBuilder to even allow thedefinition part to be type checked. It is a slightly more verbose method call. Instead of usingthe short name (theopt name) in the method call, you use a fixed name ofoption andsupply theopt value as a property. You must also specify the type directly as shown inthe following example:

import groovy.cli.TypedOptionimport groovy.transform.TypeChecked@TypeCheckedvoid testTypeChecked() {    def cli = new CliBuilder()    TypedOption<String> name = cli.option(String, opt: 'n', longOpt: 'name', 'name option')    TypedOption<Integer> age = cli.option(Integer, longOpt: 'age', 'age option')    def argz = "--name John --age 21 and some more".split()    def options = cli.parse(argz)    String n = options[name]    int a = options[age]    assert n == 'John' && a == 21    assert options.arguments() == ['and', 'some', 'more']}
Advanced CLI Usage

NOTE Advanced CLI features

CliBuilder can be thought of as a Groovy friendly wrapper on top of eitherpicocli orApache Commons CLI.If there is a feature not provided byCliBuilder that you know is supported in the underlyinglibrary, the currentCliBuilder implementation (and various Groovy language features) make it easy for youto call the underlying library methods directly. Doing so is a pragmatic way to leverage the Groovy-friendlysyntax offered byCliBuilder and yet still access some of the underlying library’s advanced features.A word of caution however; future versions ofCliBuilder could potentially use another underlying libraryand in that event, some porting work may be required for your Groovy classes and/or scripts.

Apache Commons CLI

As an example, here is some code for making use of Apache Commons CLI’s grouping mechanism:

import org.apache.commons.cli.*def cli = new CliBuilder()cli.f longOpt: 'from', 'f option'cli.u longOpt: 'until', 'u option'def optionGroup = new OptionGroup()optionGroup.with {  addOption cli.option('o', [longOpt: 'output'], 'o option')  addOption cli.option('d', [longOpt: 'directory'], 'd option')}cli.options.addOptionGroup optionGroupassert !cli.parse('-d -o'.split())(1)
1The parse will fail since only one option from a group can be used at a time.
Picocli

Below are some features available in the picocli version ofCliBuilder.

New property: errorWriter

When users of your application give invalid command line arguments,CliBuilder writes an error message and the usage help message to thestderr output stream.It doesn’t use thestdout stream to prevent the error message from being parsed when your program’soutput is used as input for another process.You can customize the destination by setting theerrorWriter to a different value.

On the other hand,CliBuilder.usage() prints the usage help message to thestdout stream.This way, when users request help (e.g. with a--help parameter),they can pipe the output to a utility likeless orgrep.

You can specify different writers for testing.Be aware that for backwards compatibility, setting thewriter property to a different valuewill setboth thewriter and theerrorWriter to the specified writer.

ANSI colors

The picocli version of CliBuilder renders the usage help message in ANSI colors on supported platforms automatically.If desired you cancustomize this.(An example follows below.)

New property: name

As before, you can set the synopsis of the usage help message with theusage property.You may be interested in a small improvement:if you only set the commandname, a synopsis will be generated automatically,with repeating elements followed by…​ and optional elements surrounded with[ and].(An example follows below.)

New property: usageMessage

This property exposes aUsageMessageSpec object from the underlying picocli library,which gives fine-grained control over various sections of the usage help message. For example:

def cli = new CliBuilder()cli.name = "myapp"cli.usageMessage.with {    headerHeading("@|bold,underline Header heading:|@%n")    header("Header 1", "Header 2")                     // before the synopsis    synopsisHeading("%n@|bold,underline Usage:|@ ")    descriptionHeading("%n@|bold,underline Description heading:|@%n")    description("Description 1", "Description 2")      // after the synopsis    optionListHeading("%n@|bold,underline Options heading:|@%n")    footerHeading("%n@|bold,underline Footer heading:|@%n")    footer("Footer 1", "Footer 2")}cli.a('option a description')cli.b('option b description')cli.c(args: '*', 'option c description')cli.usage()

Gives this output:

usageMessageSpec

Property: parser

Theparser property gives access to the picocliParserSpec object that can be used to customize the parser behavior.

This can be useful when theCliBuilder options to control the parser are not fine-grained enough.For example, for backward compatibility with the Commons CLI implementation ofCliBuilder, by defaultCliBuilder stops looking for options when an unknown option is encountered, and subsequent command line arguments are treated as positional parameters.CliBuilder provides astopAtNonOption property, and by setting this tofalse you can make the parser more strict, so an unknown option results inerror: Unknown option: '-x'.

But what if you want to treat unknown options as positional parameters, and still process subsequent command line arguments as options?

This can be accomplished with theparser property.For example:

def cli = new CliBuilder()cli.parser.stopAtPositional(false)cli.parser.unmatchedOptionsArePositionalParams(true)// ...def opts = cli.parse(args)// ...

See thedocumentation for details.

Map options

Finally, if your application has options that are key-value pairs, you may be interested in picocli’s support for maps. For example:

import java.util.concurrent.TimeUnitimport static java.util.concurrent.TimeUnit.DAYSimport static java.util.concurrent.TimeUnit.HOURSdef cli = new CliBuilder()cli.D(args: 2,   valueSeparator: '=', 'the old way')(1)cli.X(type: Map, 'the new way')(2)cli.Z(type: Map, auxiliaryTypes: [TimeUnit, Integer].toArray(), 'typed map')(3)def options = cli.parse('-Da=b -Dc=d -Xx=y -Xi=j -ZDAYS=2 -ZHOURS=23'.split())(4)assert options.Ds == ['a', 'b', 'c', 'd'](5)assert options.Xs == [ 'x':'y', 'i':'j' ](6)assert options.Zs == [ (DAYS as TimeUnit):2, (HOURS as TimeUnit):23 ](7)
1Previously,key=value pairs were split up into parts and added to a list
2Picocli map support: simply specifyMap as the type of the option
3You can even specify the type of the map elements
4To compare, let’s specify two key-value pairs for each option
5Previously, all key-value pairs end up in a list and it is up to the application to work with this list
6Picocli returns the key-value pairs as aMap
7Both keys and values of the map can be strongly typed

Controlling the Picocli version

To use a specific version of picocli, add a dependency to that version in your build configuration.If running scripts using a pre-installed version of Groovy, use the@Grab annotation to control the version of picocli to use inCliBuilder.

@GrabConfig(systemClassLoader=true)@Grab('info.picocli:picocli:4.2.0')import groovy.cli.picocli.CliBuilderdef cli = new CliBuilder()
ObjectGraphBuilder

ObjectGraphBuilder is a builder for an arbitrary graph of beans thatfollow the JavaBean convention. It is in particular useful for creating test data.

Let’s start with a list of classes that belong to your domain:

package com.acmeclass Company {    String name    Address address    List employees = []}class Address {    String line1    String line2    int zip    String state}class Employee {    String name    int employeeId    Address address    Company company}

Then usingObjectGraphBuilder building aCompany with three employees is aseasy as:

def builder = new ObjectGraphBuilder()(1)builder.classLoader = this.class.classLoader(2)builder.classNameResolver = "com.acme"(3)def acme = builder.company(name: 'ACME') {(4)    3.times {        employee(id: it.toString(), name: "Drone $it") {(5)            address(line1:"Post street")(6)        }    }}assert acme != nullassert acme instanceof Companyassert acme.name == 'ACME'assert acme.employees.size() == 3def employee = acme.employees[0]assert employee instanceof Employeeassert employee.name == 'Drone 0'assert employee.address instanceof Address
1creates a new object graph builder
2sets the classloader where the classes will be resolved
3sets the base package name for classes to be resolved
4creates aCompany instance
5with 3Employee instances
6each of them having a distinctAddress

Behind the scenes, the object graph builder:

  • will try to match a node name into aClass, using a defaultClassNameResolver strategy that requires a package name

  • then will create an instance of the appropriate class using a defaultNewInstanceResolver strategy that calls a no-arg constructor

  • resolves the parent/child relationship for nested nodes, involving two other strategies:

    • RelationNameResolver will yield the name of the child property in the parent, and the name of the parent propertyin the child (if any, in this case,Employee has a parent property aptly namedcompany)

    • ChildPropertySetter will insert the child into the parent taking into account if the child belongs to aCollectionor not (in this caseemployees should be a list ofEmployee instances inCompany).

All 4 strategies have a default implementation that work as expected ifthe code follows the usual conventions for writing JavaBeans. In case any of your beans or objects do not follow the conventionyou may plug your own implementation of each strategy. For example imagine that you need to build a class which isimmutable:

@Immutableclass Person {    String name    int age}

Then if you try to create aPerson with the builder:

def person = builder.person(name:'Jon', age:17)

It will fail at runtime with:

Cannot set readonly property: name for class: com.acme.Person

Fixing this can be done by changing the new instance strategy:

builder.newInstanceResolver = { Class klazz, Map attributes ->    if (klazz.getConstructor(Map)) {        def o = klazz.newInstance(attributes)        attributes.clear()        return o    }    klazz.newInstance()}

ObjectGraphBuilder supports ids per node, meaningthat you can store a reference to a node in the builder. This isuseful when multiple objects reference the same instance. Because aproperty namedid may be of business meaning in some domain modelsObjectGraphBuilder has a strategy namedIdentifierResolver that youmay configure to change the default name value. The same mayhappen with the property used for referencing a previously savedinstance, a strategy namedReferenceResolver will yield theappropriate value (default is `refId'):

def company = builder.company(name: 'ACME') {    address(id: 'a1', line1: '123 Groovy Rd', zip: 12345, state: 'JV')(1)    employee(name: 'Duke', employeeId: 1, address: a1)(2)    employee(name: 'John', employeeId: 2 ){      address( refId: 'a1' )(3)    }}
1an address can be created with anid
2an employee can reference the address directly with its id
3or use therefId attribute corresponding to theid of the corresponding address

Its worth mentioning that you cannot modify the properties of areferenced bean.

FileTreeBuilder

groovy.util.FileTreeBuilder is a builder for generating a file directory structure from a specification. For example, to create the following tree:

 src/  |--- main  |     |--- groovy  |            |--- Foo.groovy  |--- test        |--- groovy               |--- FooTest.groovy

You can use aFileTreeBuilder like this:

tmpDir = File.createTempDir()def fileTreeBuilder = new FileTreeBuilder(tmpDir)fileTreeBuilder.dir('src') {    dir('main') {       dir('groovy') {          file('Foo.groovy', 'println "Hello"')       }    }    dir('test') {       dir('groovy') {          file('FooTest.groovy', 'class FooTest extends groovy.test.GroovyTestCase {}')       }    } }

To check that everything worked as expected we use the following `assert`s:

assert new File(tmpDir, '/src/main/groovy/Foo.groovy').text == 'println "Hello"'assert new File(tmpDir, '/src/test/groovy/FooTest.groovy').text == 'class FooTest extends groovy.test.GroovyTestCase {}'

FileTreeBuilder also supports a shorthand syntax:

tmpDir = File.createTempDir()def fileTreeBuilder = new FileTreeBuilder(tmpDir)fileTreeBuilder.src {    main {       groovy {          'Foo.groovy'('println "Hello"')       }    }    test {       groovy {          'FooTest.groovy'('class FooTest extends groovy.test.GroovyTestCase {}')       }    } }

This produces the same directory structure as above, as shown by these `assert`s:

assert new File(tmpDir, '/src/main/groovy/Foo.groovy').text == 'println "Hello"'assert new File(tmpDir, '/src/test/groovy/FooTest.groovy').text == 'class FooTest extends groovy.test.GroovyTestCase {}'
Creating a builder

While Groovy has many built-in builders, the builder pattern is so common,you will no doubt eventually come across a building requirement thathasn’t been catered for by those built-in builders.The good news is that you can build your own.You can do everything from scratch by relying on Groovy’smetaprogramming capabilities. Alternatively, theBuilderSupportandFactoryBuilderSupport classes make designing your ownbuilders much easier.

BuilderSupport

One approach to building a builder is to subclassBuilderSupport.With this approach, the general idea is to override one or more of a number oflifecycle methods includingsetParent,nodeCompleted and some orall of thecreateNode methods from theBuilderSupport abstract class.

As an example, suppose we want to create a builder oftracking athletic training programs. Each program is madeup of a number of sets and each set has its own steps.A step might itself be a set of smaller steps.For eachset orstep, we might wish to recordthedistance required (ortime), whether torepeatthe steps a certain number of times, whether to take abreakbetween each step and so forth.

For the simplicity of this example, we’ll capture the trainingprogramming using maps and lists. A set has a list of steps.Information likerepeat count ordistance will be trackedin a map of attributes for each step and set.

The builder implementation is as follows:

  • Override a couple of thecreateNode methods.We’ll create a map capturing the set name, an empty list of steps,and potentially some attributes.

  • Whenever we complete a node we’ll add the node to the list of stepsfor the parent (if any).

The code looks like this:

class TrainingBuilder1 extends BuilderSupport {    protected createNode(name) {        [name: name, steps: []]    }    protected createNode(name, Map attributes) {        createNode(name) + attributes    }    void nodeCompleted(maybeParent, node) {        if (maybeParent) maybeParent.steps << node    }    // unused lifecycle methods    protected void setParent(parent, child) { }    protected createNode(name, Map attributes, value) { }    protected createNode(name, value) { }}

Next, we’ll write a little helper method which recursivelyadds up the distances of all substeps, accounting for repeated stepsas needed.

def total(map) {    if (map.distance) return map.distance    def repeat = map.repeat ?: 1    repeat * map.steps.sum{ total(it) }}

Finally, we can now use our builder and helper method to create aswimming training program and check its total distance:

def training = new TrainingBuilder1()def monday = training.swimming {    warmup(repeat: 3) {        freestyle(distance: 50)        breaststroke(distance: 50)    }    endurance(repeat: 20) {        freestyle(distance: 50, break: 15)    }    warmdown {        kick(distance: 100)        choice(distance: 100)    }}assert 1500 == total(monday)
FactoryBuilderSupport

A second approach to building a builder is to subclassFactoryBuilderSupport.This builder has similar goals toBuilderSupport but with extra featuresto simplify domain class construction.

With this approach, the general idea is to override one or more of a number oflifecycle methods includingresolveFactory,nodeCompleted andpostInstantiate methods from theFactoryBuilderSupport abstract class.

We’ll use the same example as for the previousBuilderSupport example;a builder of tracking athletic training programs.

For this example, rather than capturing the trainingprogramming using maps and lists, we’ll use some simple domain classes.

The builder implementation is as follows:

  • Override theresolveFactory method to return a specialfactory which returns classes by capitalizing the names used in our mini DSL.

  • Whenever we complete a node we’ll add the node to the list of stepsfor the parent (if any).

The code, including the code for the special factory class, looks like this:

import static org.apache.groovy.util.BeanUtils.capitalizeclass TrainingBuilder2 extends FactoryBuilderSupport {    def factory = new TrainingFactory(loader: getClass().classLoader)    protected Factory resolveFactory(name, Map attrs, value) {        factory    }    void nodeCompleted(maybeParent, node) {        if (maybeParent) maybeParent.steps << node    }}class TrainingFactory extends AbstractFactory {    ClassLoader loader    def newInstance(FactoryBuilderSupport fbs, name, value, Map attrs) {        def clazz = loader.loadClass(capitalize(name))        value ? clazz.newInstance(value: value) : clazz.newInstance()    }}

Rather than using lists and maps, we’ll have some simple domain classesand related traits:

trait HasDistance {    int distance}trait Container extends HasDistance {    List steps = []    int repeat}class Cycling implements Container { }class Interval implements Container { }class Sprint implements HasDistance {}class Tempo implements HasDistance {}

Just like for theBuilderSupport example, it is useful to have a helper methodto calculate the total distance covered during the training session.The implementation is very similar to our earlier example, but is adjustedto work well with our newly defined traits.

def total(HasDistance c) {    c.distance}def total(Container c) {    if (c.distance) return c.distance    def repeat = c.repeat ?: 1    repeat * c.steps.sum{ total(it) }}

Finally, we can now use our new builder and helper methods to create acycling training program and check its total distance:

def training = new TrainingBuilder2()def tuesday = training.cycling {    interval(repeat: 5) {        sprint(distance: 400)        tempo(distance: 3600)    }}assert 20000 == total(tuesday)

3.22. Working with JMX

3.22.1. Introduction

TheJava Management Extensions (JMX)technology provides a standard way of managing resources such as applications, devices, and services on the JDK.Each resource to be managed is represented by aManaged Bean (orMBean).Given that Groovy sits directly on top of Java, Groovy can leverage the tremendous amount of work alreadydone for JMX with Java. In addition, Groovy provides aGroovyMBean class, in thegroovy-jmx module,which makes an MBean look like a normal Groovy object and simplifies Groovy code for interacting with MBeans.For example, the following code:

println server.getAttribute(beanName, 'Age')server.setAttribute(beanName, new Attribute('Name', 'New name'))Object[] params = [5, 20]String[] signature = [Integer.TYPE, Integer.TYPE]println server.invoke(beanName, 'add', params, signature)

can be simplified to:

def mbean = new GroovyMBean(server, beanName)println mbean.Agembean.Name = 'New name'println mbean.add(5, 20)

The remainder of this page shows you how to:

  • Monitor the JVM using MXBeans

  • Monitor Apache Tomcat and display statistics

  • Monitor Oracle OC4J and display information

  • Monitor BEA WebLogic and display information

  • Leverage Spring’s MBean annotation support to export your Groovy beans as MBeans

3.22.2. Monitoring the JVM

MBeans are not accessed directly by an application but are managed by a repository called anMBean server. Java includes a special MBean server called theplatform MBean server, which is built into the JVM. Platform MBeans are registered in this server using unique names.

You can monitor the JVM through its platform MBeans with the following code:

import java.lang.management.*def os = ManagementFactory.operatingSystemMXBeanprintln """OPERATING SYSTEM:\tarchitecture = $os.arch\tname = $os.name\tversion = $os.version\tprocessors = $os.availableProcessors"""def rt = ManagementFactory.runtimeMXBeanprintln """RUNTIME:\tname = $rt.name\tspec name = $rt.specName\tvendor = $rt.specVendor\tspec version = $rt.specVersion\tmanagement spec version = $rt.managementSpecVersion"""def cl = ManagementFactory.classLoadingMXBeanprintln """CLASS LOADING SYSTEM:\tisVerbose = ${cl.isVerbose()}\tloadedClassCount = $cl.loadedClassCount\ttotalLoadedClassCount = $cl.totalLoadedClassCount\tunloadedClassCount = $cl.unloadedClassCount"""def comp = ManagementFactory.compilationMXBeanprintln """COMPILATION:\ttotalCompilationTime = $comp.totalCompilationTime"""def mem = ManagementFactory.memoryMXBeandef heapUsage = mem.heapMemoryUsagedef nonHeapUsage = mem.nonHeapMemoryUsageprintln """MEMORY:HEAP STORAGE:\tcommitted = $heapUsage.committed\tinit = $heapUsage.init\tmax = $heapUsage.max\tused = $heapUsage.usedNON-HEAP STORAGE:\tcommitted = $nonHeapUsage.committed\tinit = $nonHeapUsage.init\tmax = $nonHeapUsage.max\tused = $nonHeapUsage.used"""ManagementFactory.memoryPoolMXBeans.each { mp ->    println "\tname: " + mp.name    String[] mmnames = mp.memoryManagerNames    mmnames.each{ mmname ->        println "\t\tManager Name: $mmname"    }    println "\t\tmtype = $mp.type"    println "\t\tUsage threshold supported = " + mp.isUsageThresholdSupported()}println()def td = ManagementFactory.threadMXBeanprintln "THREADS:"td.allThreadIds.each { tid ->    println "\tThread name = ${td.getThreadInfo(tid).threadName}"}println()println "GARBAGE COLLECTION:"ManagementFactory.garbageCollectorMXBeans.each { gc ->    println "\tname = $gc.name"    println "\t\tcollection count = $gc.collectionCount"    println "\t\tcollection time = $gc.collectionTime"    String[] mpoolNames = gc.memoryPoolNames    mpoolNames.each { mpoolName ->        println "\t\tmpool name = $mpoolName"    }}

When run, you will see something like this:

OPERATING SYSTEM:            architecture = amd64            name = Windows 10            version = 10.0            processors = 12RUNTIME:            name = 724176@QUOKKA            spec name = Java Virtual Machine Specification            vendor = Oracle Corporation            spec version = 11            management spec version = 2.0CLASS LOADING SYSTEM:            isVerbose = false            loadedClassCount = 6962            totalLoadedClassCount = 6969            unloadedClassCount = 0COMPILATION:            totalCompilationTime = 7548MEMORY:            HEAP STORAGE:            committed = 645922816            init = 536870912            max = 8560574464            used = 47808352            NON-HEAP STORAGE:            committed = 73859072            init = 7667712            max = -1            used = 70599520name: CodeHeap 'non-nmethods'Manager Name: CodeCacheManagermtype = Non-heap memoryUsage threshold supported = truename: MetaspaceManager Name: Metaspace Managermtype = Non-heap memoryUsage threshold supported = truename: CodeHeap 'profiled nmethods'Manager Name: CodeCacheManagermtype = Non-heap memoryUsage threshold supported = truename: Compressed Class SpaceManager Name: Metaspace Managermtype = Non-heap memoryUsage threshold supported = truename: G1 Eden SpaceManager Name: G1 Old GenerationManager Name: G1 Young Generationmtype = Heap memoryUsage threshold supported = falsename: G1 Old GenManager Name: G1 Old GenerationManager Name: G1 Young Generationmtype = Heap memoryUsage threshold supported = truename: G1 Survivor SpaceManager Name: G1 Old GenerationManager Name: G1 Young Generationmtype = Heap memoryUsage threshold supported = falsename: CodeHeap 'non-profiled nmethods'Manager Name: CodeCacheManagermtype = Non-heap memoryUsage threshold supported = trueTHREADS:Thread name = Reference HandlerThread name = FinalizerThread name = Signal DispatcherThread name = Attach ListenerThread name = Common-CleanerThread name = Java2D DisposerThread name = AWT-ShutdownThread name = AWT-WindowsThread name = Image Fetcher 0Thread name = AWT-EventQueue-0Thread name = D3D Screen UpdaterThread name = DestroyJavaVMThread name = TimerQueueThread name = Thread-0GARBAGE COLLECTION:name = G1 Young Generationcollection count = 6collection time = 69mpool name = G1 Eden Spacempool name = G1 Survivor Spacempool name = G1 Old Genname = G1 Old Generationcollection count = 0collection time = 0mpool name = G1 Eden Spacempool name = G1 Survivor Spacempool name = G1 Old Gen

3.22.3. Monitoring Tomcat

First start upTomcat with JMX monitoring enabled by setting the following:

set JAVA_OPTS=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9004\ -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false

You can do this in your startup script and may choose any available port, we used 9004.

The following code uses JMX to discover the available MBeans in the running Tomcat, determine which are web modules,extract the processing time for each web module and displays the result in a graph using JFreeChart:

import groovy.swing.SwingBuilderimport groovy.jmx.GroovyMBeanimport javax.management.ObjectNameimport javax.management.remote.JMXConnectorFactory as JmxFactoryimport javax.management.remote.JMXServiceURL as JmxUrlimport javax.swing.WindowConstants as WCimport org.jfree.chart.ChartFactoryimport org.jfree.data.category.DefaultCategoryDataset as Datasetimport org.jfree.chart.plot.PlotOrientation as Orientationdef serverUrl = 'service:jmx:rmi:///jndi/rmi://localhost:9004/jmxrmi'def server = JmxFactory.connect(new JmxUrl(serverUrl)).MBeanServerConnectiondef serverInfo = new GroovyMBean(server, 'Catalina:type=Server').serverInfoprintln "Connected to: $serverInfo"def query = new ObjectName('Catalina:*')String[] allNames = server.queryNames(query, null)def modules = allNames.findAll { name ->    name.contains('j2eeType=WebModule')}.collect{ new GroovyMBean(server, it) }println "Found ${modules.size()} web modules. Processing ..."def dataset = new Dataset()modules.each { m ->    println m.name()    dataset.addValue m.processingTime, 0, m.path}def labels = ['Time per Module', 'Module', 'Time']def options = [false, true, true]def chart = ChartFactory.createBarChart(*labels, dataset,                Orientation.VERTICAL, *options)def swing = new SwingBuilder()def frame = swing.frame(title:'Catalina Module Processing Time', defaultCloseOperation:WC.DISPOSE_ON_CLOSE) {    panel(id:'canvas') { rigidArea(width:800, height:350) }}frame.pack()frame.show()chart.draw(swing.canvas.graphics, swing.canvas.bounds)

When run, we will see a trace of progress being made:

Connected to: Apache Tomcat/9.0.37Found 5 web modules. Processing ...Catalina:j2eeType=WebModule,name=//localhost/docs,J2EEApplication=none,J2EEServer=noneCatalina:j2eeType=WebModule,name=//localhost/manager,J2EEApplication=none,J2EEServer=noneCatalina:j2eeType=WebModule,name=//localhost/,J2EEApplication=none,J2EEServer=noneCatalina:j2eeType=WebModule,name=//localhost/examples,J2EEApplication=none,J2EEServer=noneCatalina:j2eeType=WebModule,name=//localhost/host-manager,J2EEApplication=none,J2EEServer=none

The output will look like this:

catalina

Note: if you get errors running this script, see theTroubleshooting section below.

3.22.4. OC4J Example

Here is a script to access OC4J and print out some information about the server, its runtime and (as an example) the configured JMS destinations:

import javax.management.remote.*import oracle.oc4j.admin.jmx.remote.api.JMXConnectorConstantdef serverUrl = new JMXServiceURL('service:jmx:rmi://localhost:23791')def serverPath = 'oc4j:j2eeType=J2EEServer,name=standalone'def jvmPath = 'oc4j:j2eeType=JVM,name=single,J2EEServer=standalone'def provider = 'oracle.oc4j.admin.jmx.remote'def credentials = [    (JMXConnectorConstant.CREDENTIALS_LOGIN_KEY): 'oc4jadmin',    (JMXConnectorConstant.CREDENTIALS_PASSWORD_KEY): 'admin']def env = [    (JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES): provider,    (JMXConnector.CREDENTIALS): credentials]def server = JmxFactory.connect(serverUrl, env).MBeanServerConnectiondef serverInfo = new GroovyMBean(server, serverPath)def jvmInfo = new GroovyMBean(server, jvmPath)println """Connected to $serverInfo.node. \Server started ${new Date(serverInfo.startTime)}.OC4J version:  $serverInfo.serverVersion from $serverInfo.serverVendorJVM version:   $jvmInfo.javaVersion from $jvmInfo.javaVendorMemory usage:  $jvmInfo.freeMemory bytes free, \$jvmInfo.totalMemory bytes total"""def query = new javax.management.ObjectName('oc4j:*')String[] allNames = server.queryNames(query, null)def dests = allNames.findAll { name ->    name.contains('j2eeType=JMSDestinationResource')}.collect { new GroovyMBean(server, it) }println "Found ${dests.size()} JMS destinations. Listing ..."dests.each { d -> println "$d.name: $d.location" }

Here is the result of running this script:

Connected to LYREBIRD. Server started Thu May 31 21:04:54 EST 2007.OC4J version:  11.1.1.0.0 from Oracle Corp.JVM version:   1.6.0_01 from Sun Microsystems Inc.Memory usage:  8709976 bytes free, 25153536 bytes totalFound 5 JMS destinations. Listing ...Demo Queue: jms/demoQueueDemo Topic: jms/demoTopicjms/Oc4jJmsExceptionQueue: jms/Oc4jJmsExceptionQueuejms/RAExceptionQueue: jms/RAExceptionQueueOracleASRouter_store: OracleASRouter_store

As a slight variation, this script displays a pie chart of memory usage using JFreeChart:

import org.jfree.chart.ChartFactoryimport javax.swing.WindowConstants as WCimport javax.management.remote.*import oracle.oc4j.admin.jmx.remote.api.JMXConnectorConstantdef url = 'service:jmx:rmi://localhost:23791'def credentials = [:]credentials[JMXConnectorConstant.CREDENTIALS_LOGIN_KEY] = "oc4jadmin"credentials[JMXConnectorConstant.CREDENTIALS_PASSWORD_KEY] = "password"def env = [:]env[JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES] = "oracle.oc4j.admin.jmx.remote"env[JMXConnector.CREDENTIALS] = credentialsdef server = JMXConnectorFactory.connect(new JMXServiceURL(url), env).MBeanServerConnectiondef jvmInfo = new GroovyMBean(server, 'oc4j:j2eeType=JVM,name=single,J2EEServer=standalone')def piedata = new org.jfree.data.general.DefaultPieDataset()piedata.setValue "Free", jvmInfo.freeMemorypiedata.setValue "Used", jvmInfo.totalMemory - jvmInfo.freeMemorydef options = [true, true, true]def chart = ChartFactory.createPieChart('OC4J Memory Usage', piedata, *options)chart.backgroundPaint = java.awt.Color.whitedef swing = new groovy.swing.SwingBuilder()def frame = swing.frame(title:'OC4J Memory Usage', defaultCloseOperation:WC.EXIT_ON_CLOSE) {    panel(id:'canvas') { rigidArea(width:350, height:250) }}frame.pack()frame.show()chart.draw(swing.canvas.graphics, swing.canvas.bounds)

Which looks like:

oc4jpie

3.22.5. WebLogic Example

This script prints out information about the server followed by information about JMS Destinations (as an example). Many other mbeans areavailable.

import javax.management.remote.*import javax.management.*import javax.naming.Contextimport groovy.jmx.GroovyMBeandef urlRuntime = '/jndi/weblogic.management.mbeanservers.runtime'def urlBase = 'service:jmx:t3://localhost:7001'def serviceURL = new JMXServiceURL(urlBase + urlRuntime)def h = new Hashtable()h.put(Context.SECURITY_PRINCIPAL, 'weblogic')h.put(Context.SECURITY_CREDENTIALS, 'weblogic')h.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES, 'weblogic.management.remote')def server = JMXConnectorFactory.connect(serviceURL, h).MBeanServerConnectiondef domainName = new ObjectName('com.bea:Name=RuntimeService,Type=weblogic.management.mbeanservers.runtime.RuntimeServiceMBean')def rtName = server.getAttribute(domainName, 'ServerRuntime')def rt = new GroovyMBean(server, rtName)println "Server: name=$rt.Name, state=$rt.State, version=$rt.WeblogicVersion"def destFilter = Query.match(Query.attr('Type'), Query.value('JMSDestinationRuntime'))server.queryNames(new ObjectName('com.bea:*'), destFilter).each { name ->    def jms = new GroovyMBean(server, name)    println "JMS Destination: name=$jms.Name, type=$jms.DestinationType, messages=$jms.MessagesReceivedCount"}

Here is the output:

Server: name=examplesServer, state=RUNNING, version=WebLogic Server 10.0  Wed May 9 18:10:27 EDT 2007 933139JMS Destination: name=examples-jms!exampleTopic, type=Topic, messages=0JMS Destination: name=examples-jms!exampleQueue, type=Queue, messages=0JMS Destination: name=examples-jms!jms/MULTIDATASOURCE_MDB_QUEUE, type=Queue, messages=0JMS Destination: name=examplesJMSServer!examplesJMSServer.TemporaryQueue0, type=Queue, messages=68JMS Destination: name=examples-jms!quotes, type=Topic, messages=0JMS Destination: name=examples-jms!weblogic.wsee.wseeExamplesDestinationQueue, type=Queue, messages=0JMS Destination: name=examples-jms!weblogic.examples.ejb30.ExampleQueue, type=Queue, messages=0

3.22.6. Spring Example

You can also use Spring to automatically register beans as JMX aware.

Here is an example class (Calculator.groovy):

import org.springframework.jmx.export.annotation.*@ManagedResource(objectName="bean:name=calcMBean", description="Calculator MBean")public class Calculator {    private int invocations    @ManagedAttribute(description="The Invocation Attribute")    public int getInvocations() {        return invocations    }    private int base = 10    @ManagedAttribute(description="The Base to use when adding strings")    public int getBase() {        return base    }    @ManagedAttribute(description="The Base to use when adding strings")    public void setBase(int base) {        this.base = base    }    @ManagedOperation(description="Add two numbers")    @ManagedOperationParameters([        @ManagedOperationParameter(name="x", description="The first number"),        @ManagedOperationParameter(name="y", description="The second number")])    public int add(int x, int y) {        invocations++        return x + y    }    @ManagedOperation(description="Add two strings representing numbers of a particular base")    @ManagedOperationParameters([        @ManagedOperationParameter(name="x", description="The first number"),        @ManagedOperationParameter(name="y", description="The second number")])    public String addStrings(String x, String y) {        invocations++        def result = Integer.valueOf(x, base) + Integer.valueOf(y, base)        return Integer.toString(result, base)    }}

Here is the Spring configuration file (beans.xml):

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">    <bean         >        <property name="locateExistingServerIfPossible" value="true"/>    </bean>    <bean         >        <property name="assembler" ref="assembler"/>        <property name="namingStrategy" ref="namingStrategy"/>        <property name="beans">            <map>                <entry key="bean:name=defaultCalcName" value-ref="calcBean"/>            </map>        </property>        <property name="server" ref="mbeanServer"/>        <property name="autodetect" value="true"/>    </bean>    <bean         />    <!-- will create management interface using annotation metadata -->    <bean         >        <property name="attributeSource" ref="jmxAttributeSource"/>    </bean>    <!-- will pick up the ObjectName from the annotation -->    <bean         >        <property name="attributeSource" ref="jmxAttributeSource"/>    </bean>    <bean         >        <property name="base" value="10"/>    </bean></beans>

Here is a script which uses this bean and configuration:

import org.springframework.context.support.ClassPathXmlApplicationContextimport java.lang.management.ManagementFactoryimport javax.management.ObjectNameimport javax.management.Attributeimport groovy.jmx.GroovyMBean// get normal beandef ctx = new ClassPathXmlApplicationContext("beans.xml")def calc = ctx.getBean("calcBean")Thread.start {    // access bean via JMX, use a separate thread just to    // show that we could access remotely if we wanted    def server = ManagementFactory.platformMBeanServer    def mbean = new GroovyMBean(server, 'bean:name=calcMBean')    sleep 1000    assert 8 == mbean.add(7, 1)    mbean.Base = 8    assert '10' == mbean.addStrings('7', '1')    mbean.Base = 16    sleep 2000    println "Number of invocations: $mbean.Invocations"    println mbean}assert 15 == calc.add(9, 6)assert '11' == calc.addStrings('10', '1')sleep 2000assert '20' == calc.addStrings('1f', '1')

And here is the resulting output:

Number of invocations: 5MBean Name:  bean:name=calcMBeanAttributes:  (rw) int Base  (r) int InvocationsOperations:  int add(int x, int y)  java.lang.String addStrings(java.lang.String x, java.lang.String y)  int getInvocations()  int getBase()  void setBase(int p1)

You can even attach to the process while it is running withjconsole. It will look something like:

jconsole

We started the Groovy application with the-Dcom.sun.management.jmxremote JVM argument.

See also:

3.22.7. Troubleshooting

java.lang.SecurityException

If you get the following error, your container’s JMX access is password protected:

java.lang.SecurityException: Authentication failed! Credentials required

To fix that, add an environment with the credentials when connecting, like this (password has to be set before that):

def jmxEnv = nullif (password != null) {    jmxEnv = [(JMXConnector.CREDENTIALS): (String[])["monitor", password]]}def connector = JMXConnectorFactory.connect(new JMXServiceURL(serverUrl), jmxEnv)

Details for the software you are trying to monitor/manage may differ slightly. Check out the other examples using credentials above if appropriate (e.g. OC4J and WebLogic). If you still have troubles, you will have to consult the documentation for the software you are trying to monitor/manage for details on how to provide credentials.

3.22.8. JmxBuilder

JmxBuilder is a Groovy-based domain specific language for the Java Management Extension (JMX) API. It uses the builder pattern (FactoryBuilder) to create an internal DSL that facilitates the exposure of POJO’s and Groovy beans as management components via the MBean server. JmxBuilder hides the complexity of creating and exporting management beans via the JMX API and provides a set of natural Groovy constructs to interact with the JMX infrastructure.

Instantiating JmxBuilder

To start using JmxBuilder, simply make sure the jar file is on your class path. Then you can do the following in your code:

def jmx = new JmxBuilder()

That’s it! You are now ready to use the JmxBuilder.

NOTE

  • You can pass in an instance ofyour own MBeanServer to the builder (JmxBuilder(MBeanServer))

  • If no MBeanServer is specified, the builder instance will default to the underlying platform MBeanServer.

Once you have an instance of JmxBuilder, you are now ready to invoke any of its builder nodes.

JMX Connectors

Remote connectivity is a crucial part of the JMX architecture. JmxBuilder facilitates the creation of connector servers and connector clients with a minimal amount of coding.

Connector Server

JmxBuilder.connectorServer() supports the full Connector api syntax and will let you specify properties, override the URL, specify your own host, etc.

Syntax

jmx.connectorServer(    protocol:"rmi",    host:"...",    port:1099,    url:"...",    properties:[        "authenticate":true|false,        "passwordFile":"...",        "accessFile":"...",        "sslEnabled" : true | false        // any valid connector property    ])

Note that the serverConnector node will accept four ServerConnector property aliases (authenticate, passwordFile,accessFile, and sslEnabled). You can use these aliases or provided any of the RMI-supported properties.

Example - Connector Server (see correction below)

jmx.connectorServer(port: 9000).start()

The snippet above returns an RMI connector that will start listening on port 9000. By default, the builder will internally generate URL"service:jmx:rmi:///jndi/rmi://localhost:9000/jmxrmi".

NOTE: Sadly you are as likely to get something like the following when attempting to run the previous snippet of code (example is incomplete, see below):

Caught: java.io.IOException: Cannot bind to URL [rmi://localhost:9000/jmxrmi]: javax.naming.ServiceUnavailableException [Root exception is java.rmi.ConnectException: Connection refused to host: localhost; nested exception is:?????? java.net.ConnectException: Connection refused]??

This occurs on Mac and Linux (CentOS 5) with Groovy 1.6 installed. Perhaps there were assumptions made about the configuration of the /etc/hosts file?

The correct example is shown below.

Connector Example (Corrected) - Connector Server

The example above does not create the RMI registry. So, in order to export, you have to first export the RMI object registry (make sure to importjava.rmi.registry.LocateRegistry).

import java.rmi.registry.LocateRegistry//...LocateRegistry.createRegistry(9000)jmx.connectorServer(port: 9000).start()
Connector Client

JmxBuilder.connectorClient() node lets you create JMX connector client object to connect to a JMX MBean Server.

Syntax

jmx.connectorClient (    protocol:"rmi",    host:"...",    port:1099,    url:"...",)

Example - Client Connector

Creating a connector client can be done just as easily. With one line of code, you can create an instance of a JMX Connector Client as shown below.

def client = jmx.connectorClient(port: 9000)client.connect()

You can then access the MBeanServerConnection associated with the connector using:

client.getMBeanServerConnection()
JmxBuilder MBean Export

You canexport a Java object or a Groovy object with minimal coding. JmxBuilder will even find andexport dynamic Groovy methods injected at runtime.

Implicit vs Explicit Descriptors

When using the builder, you canlet JmxBuilder implicitly generate all of your MBean descriptor info. This is useful when you want to write minimal code to quickly export your beans. You can also explicitly declare all descriptor info for the bean. This gives you total control on how you want to describe every piece of information that you want to export for the underlying bean.

The JmxBuilder.export() Node

TheJmxBuilder.export() node provides a container where all management entities to be exported to the MBeanServer are placed. You can place one or more bean() or timer() nodes as children of the export() node. JmxBuilder willautomatically batch export the entities described by the nodes to the MBean server for management (see example below).

def beans = jmx.export {    bean(new Foo())    bean(new Bar())    bean(new SomeBar())}

In the code snippet above,JmxBuilder.export() will export three management beans to the MBean server.

JmxBuilder.export() Syntax

JmxBuilder.export() node supports theregistrationPolicy parameter to specify how JmxBuilder will behave to resolve bean name collision during MBean registration:

jmx.export(policy:"replace|ignore|error")orjmx.export(regPolicy:"replace|ignore|error")
  • replace - JmxBuilder.export() will replace any bean already registered with the MBean during export.

  • ignore - The bean being exported will be ignored if the same bean is already registered.

  • error - JmxBuilder.export() throws an error upon bean name collision during registration.

Integration with GroovyMBean Class

When you export an MBean to the MBeanServer,JmxBuilder will return an instance of GroovyMBean representing the management bean that have been exported by the builder. Nodes such asbean() andtimer() will return an instances of GroovyMBean when they are invoked. Theexport() node returns anarray of all of GroovyMBean[] representing all managed objects exported to the MBean server.

MBean Registration with JmxBuilder.bean()

This portion of this reference uses classRequestController to illustrate how to use JmxBuilder to export runtime management beans. The class is for illustration purpose and can be a POJO or a Groovy bean.

RequestController

class RequestController {    // constructors    RequestController() { super() }    RequestController(Map resource) { }    // attributes    boolean isStarted() { true }    int getRequestCount() { 0 }    int getResourceCount() { 0 }    void setRequestLimit(int limit) { }    int getRequestLimit() { 0 }    // operations    void start() { }    void stop() { }    void putResource(String name, Object resource) { }    void makeRequest(String res) { }    void makeRequest() { }}
Implicit Export

As mentioned earlier, you can use JmxBuilder’s flexible syntax to export any POJO/POGO with no descriptor. The builder can automatically describe all aspects of the management beans using implicit defaults. These default values can easily be overridden as we’ll see in this in the next section.

The simplest way to export a POJO or POGO is listed below.

jmx.export {    bean(new RequestController(resource: "Hello World"))}

What this does:

  • First, theJmxBuilder.export() node will export an MBean to the MBeanServer representingthe declared POJO instance.

  • The builder willgenerate a default ObjectName for the MBean and all other MBean descriptor information.

  • JmxBuilder will automatically export all declaredattributes (MBean getter/setters),constructors, andoperations on the instance.

  • The exportedattributes will haveread-only visibility.

Remember,JmxBuilder.export() returns an array of GroovyMBean[] objects for all exported instances. So, once you call JmxBuilder.export(),you have immediate access to the underlying MBean proxy (via GroovyMBean).

JmxBuilder.bean() Syntax

The JmxBuilder.bean() node supports an extensive set of descriptors to describe your bean for management. The JMX MBeanServer uses these descriptors to expose metadata about the bean exposed for management.

jmx.export {    bean(        target:bean instance,        name:ObjectName,        desc:"...",        attributes:"*",        attributes:[]        attributes:[ "AttrubuteName1","AttributeName2",...,"AttributeName_n" ]        attributes:[            "AttributeName":"*",            "AttributeName":[                desc:"...",                defaultValue:value,                writable:true|false,                editable:true|false,                onChange:{event-> // event handler}            ]        ],        constructors:"*",        constructors:[            "Constructor Name":[],            "Constructor Name":[ "ParamType1","ParamType2,...,ParamType_n" ],            "Constructor Name":[                desc:"...",                params:[                    "ParamType1":"*",                    "ParamType2":[desc:"...", name:"..."],...,                    "ParamType_n":[desc:"...", name:"..."]                ]            ]        ],        operations:"*",        operations:[ "OperationName1", "OperationName2",...,"OperationNameN" ],        operations:[            "OperationName1":"*",            "OperationName2":[ "type1","type2,"type3" ]            "OperationName3":[                desc:"...",                params:[                    "ParamType1":"*"                    "ParamType2":[desc:"...", name:"..."],...,                    "ParamType_n":[desc:"...", name:"..."]                ],                onInvoked:{event-> JmxBuilder.send(event:"", to:"")}            ]        ],        listeners:[            "ListenerName1":[event: "...", from:ObjectName, call:{event->}],            "ListenerName2":[event: "...", from:ObjectName, call:&methodPointer]        ]    )}

Instead of describing the entire node, the following section explore each attribute separately.

Bean() Node - Specifying MBean ObjectName

Using the bean() node descriptors, you can specify your own MBean ObjectName.

def ctrl = new RequestController(resource:"Hello World")def beans = jmx.export {    bean(target: ctrl, name: "jmx.tutorial:type=Object")}

The ObjectName can be specified as a String or an instance of the ObjectName.

Bean() Node - Attribute Export

JMX attributes are the setters and getters on the underlying bean. The JmxBuilder.bean() node provides several ways to flexibly describe and export MBean attributes. You can combine them however you want to achieve any level of attribute visibility. Let’s take a look.

Export All Attributes with Wildcard "*"

The following code snippetwill describe and export all attributes on the bean as read-only.JmxBuilder will use default values to describe the attributes that exported for management.

def objName = new ObjectName("jmx.tutorial:type=Object")def beans = jmx.export {    bean(target: new RequestController(),    name: objName,    attributes: "*")}
Export Attribute List

JmxBuilder will let you specify a list of attributes to export.

def objName = new ObjectName("jmx.tutorial:type=Object")def beans = jmx.export {    bean(        target: new RequestController(),        name: objName,        attributes: ["Resource", "RequestCount"]    )}

In the snippet above,only the "Resource" and "RequestCount" attributes will be exported. Again, since no descriptors are provided,JmxBuilder will use sensible defaults to describe the exported attributes.

Export Attribute with Explicit Descriptors

One of the strengths of JmxBuilder is its flexibility in describing MBean. With the builder you can describe all aspects of the MBeans attribute that you want to export to the MBeanServer (see syntax above).

def objName = new ObjectName("jmx.tutorial:type=Object")def beans = jmx.export {    bean(        target: new RequestController(),        name: objName,        attributes: [            "Resource": [desc: "The resource to request.", readable: true, writable: true, defaultValue: "Hello"],            "RequestCount": "*"        ]    )}

In the snippet above, attribute"Resource" is fully-described using all supported descriptors (i.e. desc, readable, writable, defaultValue) for a JMX attribute. However, we use the wildcard to describe attributeRequestCount and it will be exported and described using defaults.

Bean() Node - Constructor Export

JmxBuildersupports the explicit description and export of constructors defined in the underlying bean. There are several options available when exporting constructors. You can combine them however you want to achieve the desired level of manageability.

Export all Constructors with "*"

You can use the builder’s special "*"notation to export all constructors declared on the underlying bean. The builder will use default values to describe the MBean constructors.

def objName = new ObjectName("jmx.tutorial:type=Object")def beans = jmx.export {    bean(        target: new RequestController(),        name: objName,        constructors: "*"    )}
Export Constructors using Parameter Descriptor

JmxBuilder lets youtarget specific constructor to exportby describing the parameter signature. This is useful when you have several constructors with different parameter signature and you want to export specific constructors.

def objName = new ObjectName("jmx.tutorial:type=Object")def beans = jmx.export {    bean(        target: new RequestController(),        name: objName,        constructors: [            "RequestController": ["Object"]        ]    )}

Here, JmxBuilder willexport a constructor that takes one parameter of type "Object". Again, JmxBuilder will use default values to fill in the description of the constructor and the parameters.

Export Constructor with Explicit Descriptors

JmxBuilder allows you tofully-describe the constructor that you want to target for export (see syntax above).

def objName = new ObjectName("jmx.tutorial:type=Object")def beans = jmx.export {    bean(target: new RequestController(), name: objName,        constructors: [            "RequestController": [                desc: "Constructor takes param",                params: ["Object" : [name: "Resource", desc: "Resource for controller"]]            ]        ]    )}

In the code above, JmxBuilder will target a constructor that takes one parameter for export to the MBeanServer. Notice how the constructor can be fully-described using all optional descriptor keys including parameter descriptors.

Bean() Node - Operation Export

Similar to constructors, JmxBuilder supports the description and export of MBean operations using a flexible notation (see above for syntax). You can combine these notations however you want to achieve the level of operation manageability desired.

Export All Operations with "*"

You can use the builder’s special "*"notation to export all operations defined on the bean to be exposed for management. The builder will use default descriptor values for the operations being exported.

def objName = new ObjectName("jmx.tutorial:type=Object")def beans = jmx.export {    bean(        target: new RequestController(),        name: objName,        operations: "*"    )}

In this snippet, JmxBuilder willexport all bean operations and will use default values to describe them in the MBeanServer.

Export Operation List

JmxBuilder has a shorthand notation that lets you quickly target operations to be exported by providing a list of methods to export.

def objName = new ObjectName("jmx.tutorial:type=Object")def beans = jmx.export {    bean(        target: new RequestController(),        name: objName,        operations: ["start", "stop"]    )}

In the snippet above, thebuilder will only export methods start() and stop(). All other methods will be ignored. JmxBuilder will use default descriptor values to describe the operations being exported.

Export Operations by Signature

Using JmxBuilder, you can target methods to export for management using the methods' parameter signature. This is useful when you want to distinguish methods with the same name that you want to export (i.e. stop() instead of stop(boolean)).

def objName = new ObjectName("jmx.tutorial:type=Object")def beans = jmx.export {    bean(        target: new RequestController(),        name: objName,        operations: [            "makeRequest": ["String"]        ]    )}

In the snippet above, JmxBuilder wouldselect method makeRequest(String) to be exported instead of the other version makeRequest() which takes no parameter. In this shorthand context, the signature is specified as a list of type (i.e. "String").

Export Operations with Explicit Descriptors

JmxBuilder supports detailed descriptors for bean operations. You can supply deep descriptor info about any operation on your bean including a name, description, method parameters, parameter type, and parameter description.

def objName = new ObjectName("jmx.tutorial:type=Object")def beans = jmx.export {    bean(target: new RequestController(), name: objName,        operations: [            "start": [desc: "Starts request controller"],            "stop": [desc: "Stops the request controller"],            "setResource": [params: ["Object"]],            "makeRequest": [                desc: "Executes the request.",                params: [                    "String": [name: "Resource", desc: "The resource to request"]                ]            ]        ]    )}

The snippet above shows all the ways JmxBuilder allows you to describe an operation targeted for management:

  • Operationsstart() and stop() are described by the "desc" key (this is enough since there are no params).

  • In operationsetResource() uses of a shorthand version ofparams: to describe the parameters for the method.

  • makeRequest() uses the extended descriptor syntax to describe all aspects of the operation.

Embedding Descriptor

JmxBuilder supports the ability toembed descriptors directly in your Groovy class. So, instead of wrapping your description around the declared object (as we’ve seen here), you can embed your JMX descriptors directly in your class.

RequestControllerGroovy

class RequestControllerGroovy {    // attributes    boolean started    int requestCount    int resourceCount    int requestLimit    Map resources    // operations    void start() { }    void stop(){ }    void putResource(String name, Object resource) { }    void makeRequest(String res) { }    void makeRequest() { }    static descriptor = [        name: "jmx.builder:type=EmbeddedObject",        operations: ["start", "stop", "putResource"],        attributes: "*"    ]}// exportjmx.export(    bean(new RequestControllerGroovy()))

There are two things going on in the code above:

  • Groovy class RequestControllerGroovy is defined and includes astatic descriptor member. That member is used to declare a JmxBuilder descriptor to describe member of the class targeted for JMX export.

  • The second part of the code shows how to use JmxBuilder to export that class for management.

Timer Export

JMX standards mandate that the implementation of the API makes available a timer service. Since JMX is a component-based architecture, timers provide an excellent signalling mechanism to communicate to registered listener components in the MBeanServer. JmxBuilder supports the creation and export of timers using the same easy syntax we’ve seen so far.

Timer Node Syntax
timer(    name:ObjectName,    event:"...",    message:"...",    data:dataValue    startDate:"now"|dateValue    period:"99d"|"99h"|"99m"|"99s"|99    occurrences:long)

The timer() node supports several attributes:

  • name: - Required The qualified JMX ObjectName instance (or String) for the timer.

  • event: - The JMX event type string that will be broadcast with every timing signal (default"jmx.builder.event").

  • message: - An optional string value that can be sent to listeners.

  • data: - An optional object that can be sent to listeners of timing signal.

  • startDate: - When to start timer. Set of valid values [ "now", date object ]. Default is "now"

  • period: - A timer’s period expressed as either a number of millisecond or time unit (day, hour, minute, second). See description below.

  • occurrences: - A number indicating the number of time to repeat timer. Default is forever.

Exporting a Timer
def timer = jmx.timer(name: "jmx.builder:type=Timer", event: "heartbeat", period: "1s")timer.start()

This snippet abovedescribes, creates, and exports a standard JMX Timer component. Here, thetimer() nodereturns a GroovyMBean that represents the registered timer MBean in the MBeanServer.

Analternative way of exporting timers is within the JmxBuilder.export() node.

def beans = jmx.export {    timer(name: "jmx.builder:type=Timer1", event: "event.signal", period: "1s")    timer(name: "jmx.builder:type=Timer2", event: "event.log", period: "1s")}beans[0].start()beans[1].start()
Timer Period

Thetimer() node supports a flexible notation for specifying thetimer period values. You can specify the time in second, minutes, hour, and day. The default is millisecond.

  • timer(period: 100) = 100 millisecond

  • timer(period: "1s") = 1 second

  • timer(period: "1m") = 1 minute

  • timer(period: "1h") = 1 hour

  • timer(period: "1d") = 1 day

The node will automatically translate.

JmxBuilder and Events

An integral part ofJMX is itsevent model. Registered management beans cancommunicate with each other by broadcasting events on the MBeanServer’s event bus.JmxBuilder provides several ways to easily listen and react to events broadcasted on the MBeanServer’s event bus. Developers cancapture any event on the bus or throw their own to be consumed by other components registered on the MBeanServer.

Event Handling Closures

JmxBuilder leverages Groovy’s use of closures to provide simple, yet elegant, mean of reacting to JMX events. JmxBuilder supports two closure signatures:

Parameterless
callback = { ->    // event handling code here.}

JmxBuilder executes the closure and passes no information about the event that was captured on the bus.

With Event Parameter
callback = { event ->    // event handling code}

JmxBuilder will pass an"event" object to the closure using this format. The event object contains information about the event was intercepted so that it can be handled by the handler. The parameter will contain different set of info depending on the event that was captured.

Handling Attribute onChange Event

When describing attributes (see bean() node section above), you canprovide a closure (or method pointer) for callback to be executed when the value of the attribute is updated on the exported MBean. This gives developers an opportunity to listen to and react to state changes on the MBean.

jmx.export {    bean(        target: new RequestController(), name: "jmx.tutorial:type=Object",        attributes: [            "Resource": [                readable: true, writable: true,                onChange: { e ->                    println e.oldValue                    println e.newValue                }            ]        ]    )}

The sample snippet above shows how tospecify an "onChange" callback closure when describing MBean attributes. In this sample code, whenever attribute "Resource" is updated via the exported MBean, theonChange event will be executed.

Attribute onChange Event Object

When handling the attribute onChange event, the handler closure will receive an event object with the following info:

  • event.oldValue - the previous attribute value before the change event.

  • event.newValue - the new value of the attribute after the change.

  • event.attribute - the name of the attribute on which the event occurred.

  • event.attributeType - the data type of the attribute that causes the event.

  • event.sequenceNumber - a numeric value representing the sequence number of event.

  • event.timeStamp - a time stamp for the event occurrence.

Handling Operation onCall Event

Similar to mbean attributes, JmxBuilder affords developers theability to listen for operation invocation on an MBean registered in the MBeaServer. JmxBuilder accepts acallback closure that will be executed after the MBean method has invoked.

class EventHandler {    void handleStart(e){        println e    }}
def handler = new EventHandler()def beans = jmx.export {    bean(target: new RequestController(), name: "jmx.tutorial:type=Object",        operations: [            "start": [                desc:"Starts request controller",                onCall:handler.&handleStart            ]        ]    )}

The snippet aboveshows how to declare an "onCall" closure to be used as listener when operation "start()" is invoked on the MBean. This sampleuses the method pointer syntax to illustrate the versatility of JmxBuilder.

Operation onCall Event Object

When handling the operation onCall event, the callback closure will receive an event object with the following info:

  • event.event - the event type string that was broadcasted.

  • event.source - The object on which the method was invoked.

  • event.data - the data type of the attribute that causes the event.

  • event.sequenceNumber - a numeric value representing the sequence number of event.

  • event.timeStamp - a time stamp for the event occurrence.

Listener MBean

When you export an MBean with the bean() node, you can define events the MBean can listen and react to. The bean() node provides a "listeners:" attribute that lets you define event listeners that your bean can react to.

def beans = jmx.export {    timer(name: "jmx.builder:type=Timer", event: "heartbeat", period: "1s").start()    bean(target: new RequestController(), name: "jmx.tutorial:type=Object",        operations: "*",        listeners: [            heartbeat: [                from: "jmx.builder:type=Timer",                call: { e ->                    println e                }            ]        ]    )}

In the sample above, we see thesyntax for adding listeners to an exported MBean.

  • First, atimer is exported and started.

  • Then, anMBean is declared that will listen to the timer event and do something meaningful.

  • The"heartbeat:" name is arbitrary and has no correlation to the timer declared above.

  • Thesource of the eventis specified using the "from:" attribute.

You can also specify anevent type you are interested in receiving from a broadcaster (since a broadcaster can be emitting multiple events).

Listening to JMX Events

In some cases, you will want to create stand-alone event listeners (not attached to exported MBeans). JmxBuilder provides the Listener() node to let you create JMX listeners that can listen to MBeanServer events. This is useful when creating JMX client applications to monitor/manage JMX agents on remote JMX MBeanServers.

Listener Node Syntax
jmx.listener(    event: "...",    from: "object name" | ObjectName,    call: { event-> })

Here is the description of thelistener() node attributes:

  • event: An optional string that identifies the JMX event type to listen for.

  • from (required): The JMX ObjectName of the component to listen to. This can be specified as a string or an instance of ObjectName.

  • call: The closure to execute when the event is captured. This can also be specified as a Groovy method pointer.

Here is an example of JmxBuilder’s listener node:

jmx.timer(name: "jmx.builder:type=Timer", period: "1s").start()jmx.listener(    from: "jmx.builder:type=Timer",    call: { e ->        println "beep..."    })

This example shows how you can use a stand-alone listener (outside an MBean export). Here, weexport a timer with a 1 second resolution. Then, we specify a listener to that timer that will print "beep" every second.

Emitting JMX Events

JmxBuilder provides thetools needed to broadcast your own events on the MBeanServer’s event bus. There are no restrictions on the event type you can broadcast. You simplydeclare your emitter and the event type that you want to send, thenbroadcast your event at any time. Any registered component in the MBeanServer can register themselves to listen to your events.

Emitter Syntax
jmx.emitter(name:"Object:Name", event:"type")

The attributes for the node Emitter() can be summarized as follows:

  • name: an optional JMX ObjectName used to register your emitter in the MBeanServer. Default is jmx.builder:type=Emitter,name=Emitter@OBJECT_HASH_VALUE

  • event: an option string value that describes the JMX event type. Default is"jmx.builder.event.emitter".

Declare the Emitter
def emitter = jmx.emitter()

The snippetdeclares the emitter using implicit descriptor syntax. JmxBuilder will do the followings:

  • Create and register an emitter MBean with a default ObjectName.

  • Setup adefault event type with value"jmx.builder.event.emitter".

  • Return a GroovyMBean representing the emitter.

As with other nodes in the builder,you can override all keys in the emitter() node. You can specify theObjectName and theevent type.

Broadcast Event

Once you have declared your emitter, you can broadcast your event.

emitter.send()

The sample above shows theemitter sending an event, once it has been declared. Any JMX component registered in the MBeanServer can register to receive message from this emitter.

Sending Event Objects

You can optionally pass data to the receiver when you send the message.

emitter.send("Hello!")

If you use anevent listener closure (see above) that accepts a parameter, you can access that value.

3.22.9. Further JMX Information

3.23. Creating Swing UIs

Creating Swing UIs is made easy thanks to the use ofSwingBuilder.

3.24. Security

Security is a complex and multi-faceted issue and needs to be addressed in a holistic way.Groovy offers some features to improve security, but organisationsconcerned about security should already be addressing other necessary aspectssuch as network security, file-system security, operating system security, database security,passwords and potentially encryption.

Also, since Groovy runs on the JDK and optionally uses other library dependencies,users should ensure their JDK and all dependencies are up-to-date with respect tothe latest security fixes.

With regard to security issues that may affect the Groovy project itself,the project follows the Apachegeneral guidelines for handling security vulnerabilities. See also the project’ssecurity policy and list ofpast vulnerabilities.

By virtue of running on the JVM and following various Java conventions, Groovy programsoffer some of the same security features as Java programs, including:

  • programs cannot access arbitrary memory locations

  • final variables cannot be changed

  • array bounds are checked

  • class loaders perform bytecode verification when loading classes

  • casting cannot be done to an incompatible class

  • access is available to APIs for encryption and authentication

Special security support is provided through:

  • groovy.lang.GroovyShell,groovy.lang.GroovyClassLoader and other parts of the Groovy runtime fully support the Java security manager which allows you to sandbox script execution with a security policy. (Note: this functionality might be scaled back in future Groovy versions or when runningon particular JDK versions in line withJEP 411)

  • org.codehaus.groovy.control.customizers.SecureASTCustomizersecures source code by controlling what code constructs are permitted or prohibited in a code base(or part of a code base)

  • DefaultXML processing has secure processing enabled and doctype definitions disabled

  • Groovy’sSQL processing features provide support to guard against SQL injection

  • Temporary directory creation protects against known security vulnerabilities such as privilege escalation if scripts are stored in operating system temp directories

3.25. Design patterns in Groovy

Usingdesign patterns with Java is a well-established topic.Design patterns also apply to Groovy:

  • some patterns carry over directly (and can make use of normal Groovy syntax improvements for greater readability)

  • some patterns are no longer required because they are built right into the language or because Groovy supports a better way of achieving the intent of the pattern

  • some patterns that have to be expressed at the design level in other languages can be implemented directly in Groovy (due to the way Groovy can blur the distinction between design and implementation)

3.25.1. Patterns

Abstract Factory Pattern

TheAbstract Factory Pattern provides a way to encapsulate a group of individual factories that have a common theme. It embodies the intent of a normal factory, i.e. remove the need for code using an interface to know the concrete implementation behind the interface, but applies to a set of interfaces and selects an entire family of concrete classes which implement those interfaces.

As an example, I might have interfaces Button, TextField and Scrollbar. I might have WindowsButton, MacButton, FlashButton as concrete classes for Button. I might have WindowsScrollBar, MacScrollBar and FlashScrollBar as concrete implementations for ScrollBar. Using the Abstract Factory Pattern should allow me to select which windowing system (i.e. Windows, Mac, Flash) I want to use once and from then on should be able to write code that references the interfaces but is always using the appropriate concrete classes (all from the one windowing system) under the covers.

Example

Suppose we want to write a game system. We might note that many games have very similar features and control.

We decide to try to split the common and game-specific code into separate classes.

First let’s look at the game-specific code for aTwo-up game:

class TwoupMessages {    def welcome = 'Welcome to the twoup game, you start with $1000'    def done = 'Sorry, you have no money left, goodbye'}class TwoupInputConverter {    def convert(input) { input.toInteger() }}class TwoupControl {    private money = 1000    private random = new Random()    private tossWasHead() {        def next = random.nextInt()        return next % 2 == 0    }    def moreTurns() {        if (money > 0) {            println "You have $money, how much would you like to bet?"            return true        }        false    }    def play(amount) {        def coin1 = tossWasHead()        def coin2 = tossWasHead()        if (coin1 && coin2) {            money += amount            println 'You win'        } else if (!coin1 && !coin2) {            money -= amount            println 'You lose'        } else {            println 'Draw'        }    }}

Now, let’s look at the game-specific code for a number guessing game:

class GuessGameMessages {    def welcome = 'Welcome to the guessing game, my secret number is between 1 and 100'    def done = 'Correct'}class GuessGameInputConverter {    def convert(input) { input.toInteger() }}class GuessGameControl {    private lower = 1    private upper = 100    private guess = new Random().nextInt(upper - lower) + lower    def moreTurns() {        def done = (lower == guess || upper == guess)        if (!done) {            println "Enter a number between $lower and $upper"        }        !done    }    def play(nextGuess) {        if (nextGuess <= guess) {            lower = [lower, nextGuess].max()        }        if (nextGuess >= guess) {            upper = [upper, nextGuess].min()        }    }}

Now, let’s write our factory code:

def guessFactory = [messages: GuessGameMessages, control: GuessGameControl, converter: GuessGameInputConverter]def twoupFactory = [messages: TwoupMessages, control: TwoupControl, converter: TwoupInputConverter]class GameFactory {    def static factory    def static getMessages() { return factory.messages.newInstance() }    def static getControl() { return factory.control.newInstance() }    def static getConverter() { return factory.converter.newInstance() }}

The important aspect of this factory is that it allows selection of an entire family of concrete classes.

Here is how we would use the factory:

GameFactory.factory = twoupFactorydef messages = GameFactory.messagesdef control = GameFactory.controldef converter = GameFactory.converterprintln messages.welcomedef reader = new BufferedReader(new InputStreamReader(System.in))while (control.moreTurns()) {    def input = reader.readLine().trim()    control.play(converter.convert(input))}println messages.done

Note that the first line configures which family of concrete game classes we will use. It’s not important that we selected which family to use by using the factory property as shown in the first line. Other ways would be equally valid examples of this pattern. For example, we may have asked the user which game they wanted to play or determined which game from an environment setting.

With the code as shown, the game might look like this when run:

Welcome to the twoup game, you start with $1000You have 1000, how much would you like to bet?300DrawYou have 1000, how much would you like to bet?700You winYou have 1700, how much would you like to bet?1700You loseSorry, you have no money left, goodbye

If we change the first line of the script to GameFactory.factory = guessFactory, then the sample run might look like this:

Welcome to the guessing game, my secret number is between 1 and 100Enter a number between 1 and 10075Enter a number between 1 and 7535Enter a number between 1 and 3515Enter a number between 1 and 155Enter a number between 5 and 1510Correct
Adapter Pattern

TheAdapter Pattern (sometimes called the wrapper pattern) allows objects satisfying one interface to be used where another type of interface is expected. There are two typical flavours of the pattern: thedelegation flavour and theinheritance flavour.

Delegation Example

Suppose we have the following classes:

class SquarePeg {    def width}class RoundPeg {    def radius}class RoundHole {    def radius    def pegFits(peg) {        peg.radius <= radius    }    String toString() { "RoundHole with radius $radius" }}

We can ask theRoundHole class if aRoundPeg fits in it, but if we ask the same question for aSquarePeg, then it will fail because theSquarePeg class doesn’t have aradius property (i.e. doesn’t satisfy the required interface).

To get around this problem, we can create an adapter to make it appear to have the correct interface. It would look like this:

class SquarePegAdapter {    def peg    def getRadius() {        Math.sqrt(((peg.width / 2) ** 2) * 2)    }    String toString() {        "SquarePegAdapter with peg width $peg.width (and notional radius $radius)"    }}

We can use the adapter like this:

def hole = new RoundHole(radius: 4.0)(4..7).each { w ->    def peg = new SquarePegAdapter(peg: new SquarePeg(width: w))    if (hole.pegFits(peg)) {        println "peg $peg fits in hole $hole"    } else {        println "peg $peg does not fit in hole $hole"    }}

Which results in the following output:

peg SquarePegAdapter with peg width 4 (and notional radius 2.8284271247461903) fits in hole RoundHole with radius 4.0peg SquarePegAdapter with peg width 5 (and notional radius 3.5355339059327378) fits in hole RoundHole with radius 4.0peg SquarePegAdapter with peg width 6 (and notional radius 4.242640687119285) does not fit in hole RoundHole with radius 4.0peg SquarePegAdapter with peg width 7 (and notional radius 4.949747468305833) does not fit in hole RoundHole with radius 4.0
Inheritance Example

Let’s consider the same example again using inheritance. First, here are the original classes (unchanged):

class SquarePeg {    def width}class RoundPeg {    def radius}class RoundHole {    def radius    def pegFits(peg) {        peg.radius <= radius    }    String toString() { "RoundHole with radius $radius" }}

An adapter using inheritance:

class SquarePegAdapter extends SquarePeg {    def getRadius() {        Math.sqrt(((width / 2) ** 2) * 2)    }    String toString() {        "SquarePegAdapter with width $width (and notional radius $radius)"    }}

Using the adapter:

def hole = new RoundHole(radius: 4.0)(4..7).each { w ->    def peg = new SquarePegAdapter(width: w)    if (hole.pegFits(peg)) {        println "peg $peg fits in hole $hole"    } else {        println "peg $peg does not fit in hole $hole"    }}

The output:

peg SquarePegAdapter with width 4 (and notional radius 2.8284271247461903) fits in hole RoundHole with radius 4.0peg SquarePegAdapter with width 5 (and notional radius 3.5355339059327378) fits in hole RoundHole with radius 4.0peg SquarePegAdapter with width 6 (and notional radius 4.242640687119285) does not fit in hole RoundHole with radius 4.0peg SquarePegAdapter with width 7 (and notional radius 4.949747468305833) does not fit in hole RoundHole with radius 4.0
Adapting using Closures

As a variation of the previous examples, we could instead define the following interface:

interface RoundThing {    def getRadius()}

We can then define an adapter as a closure as follows:

def adapter = {    p -> [getRadius: { Math.sqrt(((p.width / 2) ** 2) * 2) }] as RoundThing}

And use it like this:

def peg = new SquarePeg(width: 4)if (hole.pegFits(adapter(peg))) {    // ... as before}
Adapting using the ExpandoMetaClass

As of Groovy 1.1, there is a built-in MetaClass which can automatically add properties and methods dynamically.

Here is how the example would work using that feature:

def peg = new SquarePeg(width: 4)peg.metaClass.radius = Math.sqrt(((peg.width / 2) ** 2) * 2)

After you create a peg object, you can simply add a property to it on the fly. No need to change the original class and no need for an adapter class.

Bouncer Pattern

TheBouncer Pattern describes usage of a method whose sole purpose is to either throw an exception (when particular conditions hold) or do nothing. Such methods are often used to defensively guard pre-conditions of a method.

When writing utility methods, you should always guard against faulty input arguments. When writing internal methods, you may be able to ensure that certain pre-conditions always hold by having sufficient unit tests in place. Under such circumstances, you may reduce the desirability to have guards on your methods.

Groovy differs from other languages in that you frequently use theassert method within your methods rather than having a large number of utility checker methods or classes.

Null Checking Example

We might have a utility method such as:

class NullChecker {    static check(name, arg) {        if (arg == null) {            throw new IllegalArgumentException(name + ' is null')        }    }}

And we would use it like this:

void doStuff(String name, Object value) {    NullChecker.check('name', name)    NullChecker.check('value', value)    // do stuff}

But a more Groovy way to do this would simply be like this:

void doStuff(String name, Object value) {    assert name != null, 'name should not be null'    assert value != null, 'value should not be null'    // do stuff}
Validation Example

As an alternative example, we might have this utility method:

class NumberChecker {    static final String NUMBER_PATTERN = "\\\\d+(\\\\.\\\\d+(E-?\\\\d+)?)?"    static isNumber(str) {        if (!str ==~ NUMBER_PATTERN) {            throw new IllegalArgumentException("Argument '$str' must be a number")        }    }    static isNotZero(number) {        if (number == 0) {            throw new IllegalArgumentException('Argument must not be 0')        }    }}

And we would use it like this:

def stringDivide(String dividendStr, String divisorStr) {    NumberChecker.isNumber(dividendStr)    NumberChecker.isNumber(divisorStr)    def dividend = dividendStr.toDouble()    def divisor = divisorStr.toDouble()    NumberChecker.isNotZero(divisor)    dividend / divisor}println stringDivide('1.2E2', '3.0')// => 40.0

But with Groovy we could just as easily use:

def stringDivide(String dividendStr, String divisorStr) {    assert dividendStr =~ NumberChecker.NUMBER_PATTERN    assert divisorStr =~ NumberChecker.NUMBER_PATTERN    def dividend = dividendStr.toDouble()    def divisor = divisorStr.toDouble()    assert divisor != 0, 'Divisor must not be 0'    dividend / divisor}
Chain of Responsibility Pattern

In the Chain of Responsibility Pattern, objects using and implementing an interface (one or more methods) are intentionally loosely coupled. A set of objects thatimplement the interface are organised in a list (or in rare cases a tree). Objects using the interface make requests from the firstimplementor object. It will decide whether to perform any action itself and whether to pass the request further down the line in the list (or tree). Sometimes a default implementation for some request is also coded into the pattern if none of the implementors respond to the request.

Example using traditional classes

In this example, the script sends requests to thelister object. Thelister points to aUnixLister object. If it can’t handle the request, it sends the request to theWindowsLister. If it can’t handle the request, it sends the request to theDefaultLister.

class UnixLister {    private nextInLine    UnixLister(next) { nextInLine = next }    def listFiles(dir) {        if (System.getProperty('os.name') == 'Linux') {            println "ls $dir".execute().text        } else {            nextInLine.listFiles(dir)        }    }}class WindowsLister {    private nextInLine    WindowsLister(next) { nextInLine = next }    def listFiles(dir) {        if (System.getProperty('os.name').startsWith('Windows')) {            println "cmd.exe /c dir $dir".execute().text        } else {            nextInLine.listFiles(dir)        }    }}class DefaultLister {    def listFiles(dir) {        new File(dir).eachFile { f -> println f }    }}def lister = new UnixLister(new WindowsLister(new DefaultLister()))lister.listFiles('Downloads')

The output will be a list of files (with slightly different format depending on the operating system).

Here is a UML representation:

ChainOfResponsibilityClasses
Example using simplifying strategies

For simple cases, consider simplifying your code by not requiring the chain of classes.Instead, use Groovy truth and the elvis operator as shown here:

String unixListFiles(dir) {    if (System.getProperty('os.name') == 'Linux') {        "ls $dir".execute().text    }}String windowsListFiles(dir) {    if (System.getProperty('os.name').startsWith('Windows')) {        "cmd.exe /c dir $dir".execute().text    }}String defaultListFiles(dir) {    new File(dir).listFiles().collect{ f -> f.name }.join('\\n')}def dir = 'Downloads'println unixListFiles(dir) ?: windowsListFiles(dir) ?: defaultListFiles(dir)

Or Groovy’s switch as shown here:

String listFiles(dir) {    switch(dir) {    case { System.getProperty('os.name') == 'Linux' }:        return "ls $dir".execute().text    case { System.getProperty('os.name').startsWith('Windows') }:        return "cmd.exe /c dir $dir".execute().text    default:        new File(dir).listFiles().collect{ f -> f.name }.join('\\n')    }}println listFiles('Downloads')

Alternatively, for Groovy 3+, consider using streams of lambdas as shown here:

Optional<String> unixListFiles(String dir) {    Optional.ofNullable(dir)        .filter(d -> System.getProperty('os.name') == 'Linux')        .map(d -> "ls $d".execute().text)}Optional<String> windowsListFiles(String dir) {    Optional.ofNullable(dir)        .filter(d -> System.getProperty('os.name').startsWith('Windows'))        .map(d -> "cmd.exe /c dir $d".execute().text)}Optional<String> defaultListFiles(String dir) {    Optional.ofNullable(dir)        .map(d -> new File(d).listFiles().collect{ f -> f.name }.join('\\n'))}def dir = 'Downloads'def handlers = [this::unixListFiles, this::windowsListFiles, this::defaultListFiles]println handlers.stream()    .map(f -> f(dir))    .filter(Optional::isPresent)    .map(Optional::get)    .findFirst()    .get()
When not to use

If your use of chain of responsibility involves frequent use of theinstanceof operator, like here:

import static Math.PI as πabstract class Shape {    String name}class Polygon extends Shape {    String name    double lengthSide    int numSides}class Circle extends Shape {    double radius}class CircleAreaCalculator {    def next    def area(shape) {        if (shape instanceof Circle) {(1)            return shape.radius ** 2 * π        } else {            next.area(shape)        }    }}class SquareAreaCalculator {    def next    def area(shape) {        if (shape instanceof Polygon && shape.numSides == 4) {(1)            return shape.lengthSide ** 2        } else {            next.area(shape)        }    }}class DefaultAreaCalculator {    def area(shape) {        throw new IllegalArgumentException("Don't know how to calculate area for $shape")    }}def chain = new CircleAreaCalculator(next: new SquareAreaCalculator(next: new DefaultAreaCalculator()))def shapes = [    new Circle(name: 'Circle', radius: 5.0),    new Polygon(name: 'Square', lengthSide: 10.0, numSides: 4)]shapes.each { println chain.area(it) }
1instanceof code smell

It could indicate that instead of using the chain of responsibility pattern, you might considerusing richer types, perhaps in combination with Groovy’s multimethods. For example, perhaps this:

// ...class Square extends Polygon {    // ...}double area(Circle c) {    c.radius ** 2 * π}double area(Square s) {    s.lengthSide ** 2}def shapes = [    new Circle(radius: 5.0),    new Square(lengthSide: 10.0, numSides: 4)]shapes.each { println area(it) }

or using more traditional object-oriented style like this:

import static Math.PI as πinterface Shape {    double area()}abstract class Polygon implements Shape {    double lengthSide    int numSides    abstract double area()}class Circle implements Shape {    double radius    double area() {        radius ** 2 * π    }}class Square extends Polygon {    // ...    double area() {        lengthSide ** 2    }}def shapes = [    new Circle(radius: 5.0),    new Square(lengthSide: 10.0, numSides: 4)]shapes.each { println it.area() }
Going further

Other variations to this pattern:

  • we could have an explicit interface in the traditional example, e.g.Lister, to statically type the implementations but because ofduck-typing this is optional

  • we could use a chain tree instead of a list, e.g.if (animal.hasBackbone()) delegate toVertebrateHandler else delegate toInvertebrateHandler

  • we could always pass down the chain even if we processed a request (no early return)

  • we could decide at some point to not respond and not pass down the chain (pre-emptive abort)

  • we could use Groovy’s meta-programming capabilities to pass unknown methods down the chain, e.g. combinechain of responsibility with the use ofmethodMissing

Command Pattern

TheCommand Pattern is a pattern forloosely coupling a client object which wants to execute a series of commands andreceiver objects which enact those commands.Instead of talking to receivers directly, clients interact with an intermediary objectwhich then relays the necessary commands to the receivers.The pattern is in common use within the JDK, for example the api:javax.swing.Action[] class in Swingdecouples swing code from receivers like buttons, menu items and panels.

The class diagram showing the typical classes is:

CommandClasses

The sequence of interactions is as shown below for an arbitrary receiver:

CommandSequence
Example with traditional classes

The relevant classes required for turning a light on and off (see the example in the earlier wikipedia reference)would be as follows:

interface Command {    void execute()}// invoker classclass Switch {    private final Map<String, Command> commandMap = new HashMap<>()    void register(String commandName, Command command) {        commandMap[commandName] = command    }    void execute(String commandName) {        Command command = commandMap[commandName]        if (!command) {            throw new IllegalStateException("no command registered for " + commandName)        }        command.execute()    }}// receiver classclass Light {    void turnOn() {        println "The light is on"    }    void turnOff() {        println "The light is off"    }}class SwitchOnCommand implements Command {    Light light    @Override // Command    void execute() {        light.turnOn()    }}class SwitchOffCommand implements Command {    Light light    @Override // Command    void execute() {        light.turnOff()    }}Light lamp = new Light()Command switchOn = new SwitchOnCommand(light: lamp)Command switchOff = new SwitchOffCommand(light: lamp)Switch mySwitch = new Switch()mySwitch.register("on", switchOn)mySwitch.register("off", switchOff)mySwitch.execute("on")mySwitch.execute("off")

Our client scripts sendsexecute commands to an intermediary and knows nothingabout any specific receivers, or any specific action method names and arguments.

Simplifying variations

Given that Groovy has first-class function support, we can do away with theactual command classes (likeSwitchOnCommand) by instead using closures as shown here:

interface Command {    void execute()}// invoker classclass Switch {    private final Map<String, Command> commandMap = [:]    void register(String commandName, Command command) {        commandMap[commandName] = command    }    void execute(String commandName) {        Command command = commandMap[commandName]        if (!command) {            throw new IllegalStateException("no command registered for $commandName")        }        command.execute()    }}// receiver classclass Light {    void turnOn() {        println 'The light is on'    }    void turnOff() {        println 'The light is off'    }}Light lamp = new Light()Switch mySwitch = new Switch()mySwitch.register("on", lamp.&turnOn)(1)mySwitch.register("off", lamp.&turnOff)(1)mySwitch.execute("on")mySwitch.execute("off")
1Command closures (here method closures) but could be lambdas/method references for Groovy 3+

We can simplify further using the JDK’s existingRunnable interfaceand using a switch map rather than a separateSwitch class as shown here:

class Light {    void turnOn() {        println 'The light is on'    }    void turnOff() {        println 'The light is off'    }}class Door {    static void unlock() {        println 'The door is unlocked'    }}Light lamp = new Light()Map<String, Runnable> mySwitch = [    on: lamp::turnOn,    off: lamp::turnOff,    unlock: Door::unlock]mySwitch.on()mySwitch.off()mySwitch.unlock()

We have added an additionalDoor receiver to illustrate how to expand the original example.Running this script results in:

The light is onThe light is offThe door is unlocked

As a variation, if the command names aren’t important to us,we can forgo using the switch map and just have a list of tasks to invoke as shown here:

// ...List<Runnable> tasks = [lamp::turnOn, lamp::turnOff, Door::unlock]tasks.each{ it.run() }
Composite Pattern

TheComposite Pattern allows you to treat single instances of an object the same way as a group of objects. The pattern is often used with hierarchies of objects. Typically, one or more methods should be callable in the same way for eitherleaf orcomposite nodes within the hierarchy. In such a case, composite nodes typically invoke the same named method for each of their children nodes.

Example

Consider this usage of the composite pattern where we want to calltoString() on eitherLeaf orComposite objects.

CompositeClasses

In Java, theComponent class is essential as it provides the type used for both leaf and composite nodes. In Groovy, because of duck-typing, we don’t need it for that purpose, however, it can still serve as a useful place to place common behaviour between the leaf and composite nodes.

For our purposes, we will assemble the following hierarchy of components.

CompositeComponents

Here is the code:

abstract class Component {    def name    def toString(indent) {        ("-" * indent) + name    }}class Composite extends Component {    private children = []    def toString(indent) {        def s = super.toString(indent)        children.each { child ->            s += "\\n" + child.toString(indent + 1)        }        s    }    def leftShift(component) {        children << component    }}class Leaf extends Component { }def root = new Composite(name: "root")root << new Leaf(name: "leaf A")def comp = new Composite(name: "comp B")root << comproot << new Leaf(name: "leaf C")comp << new Leaf(name: "leaf B1")comp << new Leaf(name: "leaf B2")println root.toString(0)

Here is the resulting output:

root-leaf A-comp B--leaf B1--leaf B2-leaf C
Decorator Pattern

TheDecorator Pattern provides a mechanism to embellish the behaviour of an object without changing its essential interface. A decorated object should be able to be substituted wherever the original (non-decorated) object was expected. Decoration typically does not involve modifying the source code of the original object and decorators should be able to be combined in flexible ways to produce objects with several embellishments.

Traditional Example

Suppose we have the followingLogger class.

class Logger {    def log(String message) {        println message    }}

There might be times when it is useful to timestamp a log message, or times when we might want to change the case of the message. We could try to build all of this functionality into ourLogger class. If we did that, theLogger class would start to be very complex. Also, everyone would obtain all of the features even when they might want only a small subset of the features. Finally, feature interaction would become quite difficult to control.

To overcome these drawbacks, we instead define two decorator classes. Uses of theLogger class are free to embellish their base logger with zero or more decorator classes in whatever order they desire. The classes look like this:

class TimeStampingLogger extends Logger {    private Logger logger    TimeStampingLogger(logger) {        this.logger = logger    }    def log(String message) {        def now = Calendar.instance        logger.log("$now.time: $message")    }}class UpperLogger extends Logger {    private Logger logger    UpperLogger(logger) {        this.logger = logger    }    def log(String message) {        logger.log(message.toUpperCase())    }}

We can use the decorators like so:

def logger = new UpperLogger(new TimeStampingLogger(new Logger()))logger.log("G'day Mate")// => Tue May 22 07:13:50 EST 2007: G'DAY MATE

You can see that we embellish the logger behaviour with both decorators. Because of the order we chose to apply the decorators, our log message comes out capitalised and the timestamp is in normal case. If we swap the order around, let’s see what happens:

logger = new TimeStampingLogger(new UpperLogger(new Logger()))logger.log('Hi There')// => TUE MAY 22 07:13:50 EST 2007: HI THERE

Now the timestamp itself has also been changed to be uppercase.

Simplifying with closures or lambdas

Closures make it easy to represent code. We can use that fact tomake a general purpose logger class that accepts the decoration code as a closure.This saves us defining many decoration classes.

class DecoratingLogger {    def decoration = Closure.IDENTITY    def log(String message) {        println decoration(message)    }}def upper = { it.toUpperCase() }def stamp = { "$Calendar.instance.time: $it" }def logger = new DecoratingLogger(decoration: stamp << upper)logger.log("G'day Mate")// Sat Aug 29 15:28:29 AEST 2020: G'DAY MATE

We can use the same approach with lambdas:

import java.util.function.Functionclass DecoratingLogger {    Function<String, String> decoration = Function.identity()    def log(String message) {        println decoration.apply(message)    }}Function<String, String> upper = s -> s.toUpperCase()Function<String, String> stamp = s -> "$Calendar.instance.time: $s"def logger = new DecoratingLogger(decoration: upper.andThen(stamp))logger.log("G'day Mate")// => Sat Aug 29 15:38:28 AEST 2020: G'DAY MATE
A touch of dynamic behaviour

Our previous decorators were specific toLogger objects. We can use Groovy’s Meta-Object Programming capabilities to create a decorator which is far more general purpose in nature. Consider this class:

class GenericLowerDecorator {    private delegate    GenericLowerDecorator(delegate) {        this.delegate = delegate    }    def invokeMethod(String name, args) {        def newargs = args.collect { arg ->            if (arg instanceof String) {                return arg.toLowerCase()            } else {                return arg            }        }        delegate.invokeMethod(name, newargs)    }}

It takes any class and decorates it so that anyString method parameter will automatically be changed to lower case.

logger = new GenericLowerDecorator(new TimeStampingLogger(new Logger()))logger.log('IMPORTANT Message')// => Tue May 22 07:27:18 EST 2007: important message

Just be careful with ordering here. The original decorators were restricted to decoratingLogger objects. This decorator works with any object type, so we can’t swap the ordering around, i.e. this won’t work:

// Can't mix and match Interface-Oriented and Generic decorators// logger = new TimeStampingLogger(new GenericLowerDecorator(new Logger()))

We could overcome this limitation be generating an appropriate Proxy type at runtime but we won’t complicate the example here.

Runtime behaviour embellishment

You can also consider using theExpandoMetaClass from Groovy 1.1 to dynamically embellish a class with behaviour. This isn’t the normal style of usage of the decorator pattern (it certainly isn’t nearly as flexible) but may help you to achieve similar results in some cases without creating a new class.

Here’s what the code looks like:

// current mechanism to enable ExpandoMetaClassGroovySystem.metaClassRegistry.metaClassCreationHandle = new ExpandoMetaClassCreationHandle()def logger = new Logger()logger.metaClass.log = { String m -> println 'message: ' + m.toUpperCase() }logger.log('x')// => message: X

This achieves a similar result to applying a single decorator but we have no way to easily apply and remove embellishments on the fly.

More dynamic decorating

Suppose we have a calculator class (Actually any class would do).

class Calc {    def add(a, b) { a + b }}

We might be interested in observing usage of the class over time. If it is buried deep within our codebase, it might be hard to determine when it is being called and with what parameters. Also, it might be hard to know if it is performing well. We can easily make a generic tracing decorator that prints out tracing information whenever any method on theCalc class is called and also provide timing information about how long it took to execute. Here is the code for the tracing decorator:

class TracingDecorator {    private delegate    TracingDecorator(delegate) {        this.delegate = delegate    }    def invokeMethod(String name, args) {        println "Calling $name$args"        def before = System.currentTimeMillis()        def result = delegate.invokeMethod(name, args)        println "Got $result in ${System.currentTimeMillis()-before} ms"        result    }}

Here is how to use the class in a script:

def tracedCalc = new TracingDecorator(new Calc())assert 15 == tracedCalc.add(3, 12)

And here is what you would see after running this script:

Calling add{3, 12}Got 15 in 31 ms
Decorating with an Interceptor

The above timing example hooks into the lifecycle of Groovy objects (viainvokeMethod). This is such an important style performing meta-programming that Groovy has special support for this style of decorating usinginterceptors.

Groovy even comes with a built-inTracingInterceptor. We can extend the built-in class like this:

class TimingInterceptor extends TracingInterceptor {    private beforeTime    def beforeInvoke(object, String methodName, Object[] arguments) {        super.beforeInvoke(object, methodName, arguments)        beforeTime = System.currentTimeMillis()    }    Object afterInvoke(Object object, String methodName, Object[] arguments, Object result) {        super.afterInvoke(object, methodName, arguments, result)        def duration = System.currentTimeMillis() - beforeTime        writer.write("Duration: $duration ms\\n")        writer.flush()        result    }}

Here is an example of using this new class:

def proxy = ProxyMetaClass.getInstance(Calc)proxy.interceptor = new TimingInterceptor()proxy.use {    assert 7 == new Calc().add(1, 6)}

And here is the output:

before Calc.ctor()after  Calc.ctor()Duration: 0 msbefore Calc.add(java.lang.Integer, java.lang.Integer)after  Calc.add(java.lang.Integer, java.lang.Integer)Duration: 2 ms
Decorating with java.lang.reflect.Proxy

If you are trying to decorate an object (i.e. just a particular instance of the class, not the class generally), then you can use Java’sjava.lang.reflect.Proxy. Groovy makes working with this easier than just Java. Below is a code sample taken out of a grails project that wraps ajava.sql.Connection so that it’s close method is a no-op:

protected Sql getGroovySql() {    final Connection con = session.connection()    def invoker = { object, method, args ->        if (method.name == "close") {            log.debug("ignoring call to Connection.close() for use by groovy.sql.Sql")        } else {            log.trace("delegating $method")            return con.invokeMethod(method.name, args)        }    } as InvocationHandler;    def proxy = Proxy.newProxyInstance( getClass().getClassLoader(), [Connection] as Class[], invoker )    return new Sql(proxy)}

If there were many methods to intercept, then this approach could be modified to look up closure in a map by method name and invoke it.

Decorating with Spring

TheSpring Framework allows decorators to be applied withinterceptors (you may have heard the termsadvice oraspect). You can leverage this mechanism from Groovy as well.

First define a class that you want to decorate (we’ll also use an interface as is normal Spring practice):

Here’s the interface:

interface Calc {    def add(a, b)}

Here’s the class:

class CalcImpl implements Calc {    def add(a, b) { a + b }}

Now, we define our wiring in a file calledbeans.xml as follows:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:lang="http://www.springframework.org/schema/lang"xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/lang https://www.springframework.org/schema/lang/spring-lang.xsd">    <bean autowire="no"         >        <property name="loggerName" value="performance"/>    </bean>    <bean/>    <bean>        <property name="beanNames" value="calc"/>        <property name="interceptorNames" value="performanceInterceptor"/>    </bean></beans>

Now, our script looks like this:

@Grab('org.springframework:spring-context:5.2.8.RELEASE')import org.springframework.context.support.ClassPathXmlApplicationContextdef ctx = new ClassPathXmlApplicationContext('beans.xml')def calc = ctx.getBean('calc')println calc.add(3, 25)

And when we run it, we see the results:

21/05/2007 23:02:35 org.springframework.aop.interceptor.PerformanceMonitorInterceptor invokeUnderTraceFINEST: StopWatch 'util.Calc.add': running time (millis) = 16

You may have to adjust yourlogging.properties file for messages at log levelFINEST to be displayed.

Asynchronous Decorators using GPars

The following example is inspired by some of the early example code for thePanini programming language.These days, you’ll see this style used with async functions in JavaScript.

@Grab('org.codehaus.gpars:gpars:0.10')import static groovyx.gpars.GParsPool.withPoolinterface Document {    void print()    String getText()}class DocumentImpl implements Document {    def document    void print() { println document }    String getText() { document }}def words(String text) {    text.replaceAll('[^a-zA-Z]', ' ').trim().split("\\\\s+")*.toLowerCase()}def avgWordLength = {    def words = words(it.text)    sprintf "Avg Word Length: %4.2f", words*.size().sum() / words.size()}def modeWord = {    def wordGroups = words(it.text).groupBy {it}.collectEntries { k, v -> [k, v.size()] }    def maxSize = wordGroups*.value.max()    def maxWords = wordGroups.findAll { it.value == maxSize }    "Mode Word(s): ${maxWords*.key.join(', ')} ($maxSize occurrences)"}def wordCount = { d -> "Word Count: " + words(d.text).size() }def asyncDecorator(Document d, Closure c) {    ProxyGenerator.INSTANCE.instantiateDelegate([print: {        withPool {            def result = c.callAsync(d)            d.print()            println result.get()        }    }], [Document], d)}Document d = asyncDecorator(asyncDecorator(asyncDecorator(        new DocumentImpl(document:"This is the file with the words in it\\n\\t\\nDo you see the words?\\n"),//        new DocumentImpl(document: new File('AsyncDecorator.groovy').text),        wordCount), modeWord), avgWordLength)d.print()
Delegation Pattern

TheDelegation Pattern is a technique where an object’s behavior (public methods) is implemented by delegating responsibility to one or more associated objects.

Groovy allows the traditional style of applying the delegation pattern, e.g. seeReplace Inheritance with Delegation.

Implement Delegation Pattern using ExpandoMetaClass

Thegroovy.lang.ExpandoMetaClass allows usage of this pattern to be encapsulated in a library. This allows Groovy to emulate similar libraries available for the Ruby language.

Consider the following library class:

class Delegator {    private targetClass    private delegate    Delegator(targetClass, delegate) {        this.targetClass = targetClass        this.delegate = delegate    }    def delegate(String methodName) {        delegate(methodName, methodName)    }    def delegate(String methodName, String asMethodName) {        targetClass.metaClass."$asMethodName" = delegate.&"$methodName"    }    def delegateAll(String[] names) {        names.each { delegate(it) }    }    def delegateAll(Map names) {        names.each { k, v -> delegate(k, v) }    }    def delegateAll() {        delegate.class.methods*.name.each { delegate(it) }    }}

With this in your classpath, you can now apply the delegation pattern dynamically as shown in the following examples. First, consider we have the following classes:

class Person {    String name}class MortgageLender {    def borrowAmount(amount) {       "borrow \\$$amount"    }    def borrowFor(thing) {       "buy \\$thing"    }}def lender = new MortgageLender()def delegator = new Delegator(Person, lender)

We can now use thedelegator to automatically borrow methods from thelender object to extend thePerson class. We can borrow the methods as is or with a rename:

delegator.delegate 'borrowFor'delegator.delegate 'borrowAmount', 'getMoney'def p = new Person()println p.borrowFor('present')   // => buy presentprintln p.getMoney(50)

The first line above, adds theborrowFor method to thePerson class by delegating to thelender object. The second line adds agetMoney method to thePerson class by delegating to thelender object’sborrowAmount method.

Alternatively, we could borrow multiple methods like this:

delegator.delegateAll 'borrowFor', 'borrowAmount'

Which adds these two methods to thePerson class.

Or if we want all the methods, like this:

delegator.delegateAll()

Which will make all the methods in the delegate object available in thePerson class.

Alternatively, we can use a map notation to rename multiple methods:

delegator.delegateAll borrowAmount:'getMoney', borrowFor:'getThing'
Implement Delegation Pattern using @Delegate annotation

Since version 1.6 you can use the built-in delegation mechanism which is based on AST transformation.

This make delegation even easier:

class Person {    def name    @Delegate MortgageLender mortgageLender = new MortgageLender()}class MortgageLender {    def borrowAmount(amount) {       "borrow \\$$amount"    }    def borrowFor(thing) {       "buy $thing"    }}def p = new Person()assert "buy present" == p.borrowFor('present')assert "borrow \\$50" == p.borrowAmount(50)
Flyweight Pattern

TheFlyweight Pattern is a pattern for greatly reducing memory requirements by not requiring that heavy-weight objects be created in large numbers when dealing with systems that contain many things that are mostly the same. If for instance, a document was modelled using a complex character class that knew about unicode, fonts, positioning, etc., then the memory requirements could be quite large for large documents if each physical character in the document required its own character class instance. Instead, characters themselves might be kept within Strings and we might have one character class (or a small number such as one character class for each font type) that knew the specifics of how to deal with characters.

In such circumstances, we call the state that is shared with many other things (e.g. the character type)intrinsic state. It is captured within the heavy-weight class. The state which distinguishes the physical character (maybe just its ASCII code or Unicode) is called itsextrinsic state.

Example

First we are going to model some complex aircraft (the first being a hoax competitor of the second - though that is not relevant to the example).

class Boeing797 {    def wingspan = '80.8 m'    def capacity = 1000    def speed = '1046 km/h'    def range = '14400 km'    // ...}
b797 hoax
class Airbus380 {    def wingspan = '79.8 m'    def capacity = 555    def speed = '912 km/h'    def range = '10370 km'    // ...}
a380

If we want to model our fleet, our first attempt might involve using many instances of these heavy-weight objects. It turns out though that only a few small pieces of state (our extrinsic state) change for each aircraft, so we will have singletons for the heavy-weight objects and capture the extrinsic state (bought date and asset number in the code below) separately.

class FlyweightFactory {    static instances = [797: new Boeing797(), 380: new Airbus380()]}class Aircraft {    private type         // intrinsic state    private assetNumber  // extrinsic state    private bought       // extrinsic state    Aircraft(typeCode, assetNumber, bought) {        type = FlyweightFactory.instances[typeCode]        this.assetNumber = assetNumber        this.bought = bought    }    def describe() {        println """        Asset Number: $assetNumber        Capacity: $type.capacity people        Speed: $type.speed        Range: $type.range        Bought: $bought        """    }}def fleet = [    new Aircraft(380, 1001, '10-May-2007'),    new Aircraft(380, 1002, '10-Nov-2007'),    new Aircraft(797, 1003, '10-May-2008'),    new Aircraft(797, 1004, '10-Nov-2008')]fleet.each { p -> p.describe() }

So here, even if our fleet contained hundreds of planes, we would only have one heavy-weight object for each type of aircraft.

As a further efficiency measure, we might use lazy creation of the flyweight objects rather than create the initial map up front as in the above example.

Running this script results in:

Asset Number: 1001Capacity: 555 peopleSpeed: 912 km/hRange: 10370 kmBought: 10-May-2007Asset Number: 1002Capacity: 555 peopleSpeed: 912 km/hRange: 10370 kmBought: 10-Nov-2007Asset Number: 1003Capacity: 1000 peopleSpeed: 1046 km/hRange: 14400 kmBought: 10-May-2008Asset Number: 1004Capacity: 1000 peopleSpeed: 1046 km/hRange: 14400 kmBought: 10-Nov-2008
Iterator Pattern

TheIterator Pattern allows sequential access to the elements of an aggregate object without exposing its underlying representation.

Groovy has the iterator pattern built right in to many of its closure operators, e.g.each andeachWithIndex as well as thefor .. in loop.

For example:

def printAll(container) {    for (item in container) { println item }}def numbers = [ 1,2,3,4 ]def months = [ Mar:31, Apr:30, May:31 ]def colors = [ java.awt.Color.BLACK, java.awt.Color.WHITE ]printAll numbersprintAll monthsprintAll colors

Results in the output:

1234May=31Mar=31Apr=30java.awt.Color[r=0,g=0,b=0]java.awt.Color[r=255,g=255,b=255]

Another example:

colors.eachWithIndex { item, pos ->    println "Position $pos contains '$item'"}

Results in:

Position 0 contains 'java.awt.Color[r=0,g=0,b=0]'Position 1 contains 'java.awt.Color[r=255,g=255,b=255]'

The iterator pattern is also built in to other special operators such as theeachByte,eachFile,eachDir,eachLine,eachObject,eachMatch operators for working with streams, URLs, files, directories and regular expressions matches.

Loan my Resource Pattern

TheLoan my Resource pattern ensures that a resource is deterministically disposed of once it goes out of scope.

This pattern is built in to many Groovy helper methods. You should consider using it yourself if you need to work with resources in ways beyond what Groovy supports.

Example

Consider the following code which works with a file. First we might write some line to the file and then print its size:

def f = new File('junk.txt')f.withPrintWriter { pw ->    pw.println(new Date())    pw.println(this.class.name)}println f.size()// => 42

We could also read back the contents of the file a line at a time and print each line out:

f.eachLine { line ->    println line}// =>// Mon Jun 18 22:38:17 EST 2007// RunPattern

Note that normal JavaReader andPrintWriter objects were used under the covers by Groovy but the code writer did not have to worry about explicitly creating or closing those resources. The built-in Groovy methods loan the respective reader or writer to the closure code and then tidy up after themselves. So, you are using this pattern without having to do any work.

Sometimes however, you wish to do things slightly differently to what you can get for free using Groovy’s built-in mechanisms. You should consider utilising this pattern within your own resource-handling operations.

Consider how you might process the list of words on each line within the file. We could actually do this one too using Groovy’s built-in functions, but bear with us and assume we have to do some resource handling ourselves. Here is how we might write the code without using this pattern:

def reader = f.newReader()reader.splitEachLine(' ') { wordList ->    println wordList}reader.close()// =>// [ "Mon", "Jun", "18", "22:38:17", "EST", "2007" ]// [ "RunPattern" ]

Notice that we now have an explicit call toclose() in our code. If we didn’t code it just right (here we didn’t surround the code in atry …​ finally block, we run the risk of leaving the file handle open.

Let’s now apply the loan pattern. First, we’ll write a helper method:

def withListOfWordsForEachLine(File f, Closure c) {    def r = f.newReader()    try {        r.splitEachLine(' ', c)    } finally {        r?.close()    }}

Now, we can re-write our code as follows:

withListOfWordsForEachLine(f) { wordList ->    println wordList}// =>// [ "Mon", "Jun", "18", "22:38:17", "EST", "2007" ]// [ "RunPattern" ]

This is much simpler and has removed the explicitclose(). This is now catered for in one spot so we can apply the appropriate level of testing or reviewing in just one spot to be sure we have no problems.

Using Monoids

Monoids allowthe mechanics of an aggregation algorithm to be separated from the algorithm-specific logic associated with that aggregation.It is often thought to be a functional design pattern.

Perhaps, it is easiest seen with an example. Consider the code for integer sum, integer product and string concatenation.We might note various similarities:

def nums = [1, 2, 3, 4]def sum = 0(1)for (num in nums) { sum += num }(2)assert sum == 10def product = 1(1)for (num in nums) { product *= num }(2)assert product == 24def letters = ['a', 'b', 'c']def concat = ''(1)for (letter in letters) { concat += letter }(2)assert concat == 'abc'
1Initialize an aggregate counter
2Loop throw elements with for/while/iteration adjusting counter

We can remove the duplicate aggregation coding and the tease out the important differences for each algorithm.We might instead use Groovy’sinject method. This is afold operation in functional programming jargon.

assert nums.inject(0){ total, next -> total + next } == 10assert nums.inject(1){ total, next -> total * next } == 24assert letters.inject(''){ total, next -> total + next } == 'abc'

Here the first parameter is the initial value, and the supplied closure contains the algorithm-specific logic.

Similarly, for Groovy 3+, we can use the JDK stream API and lambda syntax as follows:

assert nums.stream().reduce(0, (total, next) -> total + next) == 10assert nums.stream().reduce(1, (total, next) -> total * next) == 24assert letters.stream().reduce('', (total, next) -> total + next) == 'abc'
A touch of formalism

Looking at these examples, we might think all aggregation can be supported this way.In fact, we look for certain characteristics to ensure that this aggregation pattern will apply:

  • Closure: performing the aggregation step should produce a result of the same type as the elements being aggregated.

Examples:1L + 3L produces aLong, and'foo' + 'bar' produces aString.
Non-monoid examples:'foo'.size() + 'bar'.size() (takes strings, returns an integer),the typeodd numbers with respect to addition, algorithms which don’t handle null arguments if such arguments are possible.

When using the termclosure here, we simply mean closed under the operation, not the GroovyClosure class.

  • Associativity: the order in which we apply the aggregation step should not matter.

Examples:(1 + 3) + 5 is the same as1 + (3 + 5), and('a' + 'b') + 'c' is the same as'a' + ('b' + 'c').
Non-monoid example:(10 - 5) - 3 is not equal to10 - (5 - 3) therefore integers are not a monoid with respect to subtraction.

  • Identity element (sometimes also called a 'zero' element):there should be an element which aggregated with any element returns the original element.

Examples:0 + 42 == 42,42 + 0 == 42,1 * 42 == 42, and'' + 'foo' == 'foo'.
Non-monoid example: the typenon-empty strings is not a monoid with respect to concatenation.

If your algorithm doesn’t satisfy all the monoid properties, that doesn’t mean aggregation isn’t possible.It just means that you won’t get all the benefits from monoids, which we’ll cover shortly, or you might havea little more work to do.Also, you might be able to convert your data structures slightly to turn your problem into one involving monoids.We’ll cover that topic a little later in this section.

Benefits of monoids

Consider adding the integers 10 through 16.Because the operation of addition for integers is a monoid, we already know that we can save writing codeand instead use the approach we saw in the earlierinject examples.There are some other nice properties.

Because of theclosure property,if we have a pairwise method likesum(Integer a, Integer b), then for a monoid, we can alwaysextend that method to work with a list, e.g.sum(List<Integer> nums) orsum(Integer first, Integer…​ rest).

Because ofassociativity,we can employ some interesting ways to solve the aggregation including:

  • Divide and conquer algorithms which break the problem into smaller pieces

  • Various incremental algorithms for example memoization would allow summing from 1..5to potentially start part way through be reusing a cached value of summing 1..4 if that had been calculated earlier

  • Inherent parallelization can make use of multiple cores

Let’s just look at the first of these in more detail. With a multicoreprocessor, one core could add10 plus11, another core12 plus13, and so on.We’d use theidentity element if needed (shown being added to16 in our example).Then the intermediate results could also be added together concurrently and so on until the result was reached.

MonoidAddition

We have reduced the amount of code we need to write, and we also have potential performance gains.

Here is how we might code the previous example using theGParsconcurrency and parallelism framework (two alternatives shown):

def nums = 10..16GParsPool.withPool {    assert 91 == nums.injectParallel(0){ total, next -> total + next }    assert 91 == nums.parallel.reduce(0, (total, next) -> total + next)}
Working with Non-monoids

Suppose we want to find the average of the numbers 1..10. Groovy has a built-in method for this:

assert (1..10).average() == 5.5

Now, suppose we want to build our own monoid solution instead of using the built-in version.It might seem difficult to find theidentity element. After all:

assert (0..10).average() == 5

Similarly, if we are tempted to write the pairwise aggregation closure it might be something like:

def avg = { a, b -> (a + b) / 2 }

Whatb can we use for theidentity element here so that our equation returns the original?We need to usea, but that isn’t a fixed value, so there is noidentity.

Also, associativity doesn’t hold for this initial attempt at definingavg as these examples show:

assert 6 == avg(avg(10, 2), 6)assert 7 == avg(10, avg(2, 6))

Also, what about ourclosure property? Our original numbers were integers, but our average (5.5)is not. We can solve this by making our average work for anyNumber instances, but it might not always be this easy.

It might appear that this problem is not amenable to a monoidal solution.However, there are numerous ways to bring monoids into the solution.

We can split it into two parts:

def nums = 1..10def total = nums.sum()def avg = total / nums.size()assert avg == 5.5

The calculation ofsum() can follow monoid rules and then our last step can calculate the average.We can even do a concurrent version with GPars:

withPool {    assert 5.5 == nums.sumParallel() / nums.size()}

Here, we were using the built-insum() method (andsumParallel() for the GPars example),but if you were doing it by hand, the monoid nature of that part of your calculation wouldmake it easier to write your own code for that step.

Alternatively, we can introduce a helper data structure that reworks the problem to be a monoid.Instead of just keeping the total, let’s keep a list containing the total and count of numbers.The code could look something like this:

def holder = nums    .collect{ [it, 1] }    .inject{ a, b -> [a[0] + b[0], a[1] + b[1]] }def avg = holder[0] / holder[1]assert avg == 5.5

Or, to be a little fancier, we could introduce a class for our data structure and even calculateconcurrently:

class AverageHolder {    int total    int count    AverageHolder plus(AverageHolder other) {        return new AverageHolder(total: total + other.total,                                 count: count + other.count)    }    static final AverageHolder ZERO =        new AverageHolder(total: 0, count: 0)}def asHolder = {    it instanceof Integer ? new AverageHolder(total: it, count : 1) : it}def pairwiseAggregate = { aggregate, next ->    asHolder(aggregate) + asHolder(next)}withPool {    def holder = nums.injectParallel(AverageHolder.ZERO, pairwiseAggregate)    def avg = holder.with{ total / count }    assert avg == 5.5}
Null Object Pattern

TheNull Object Pattern involves using a special object place-marker object representing null. Typically, if you have a reference to null, you can’t invokereference.field orreference.method() You receive the dreadedNullPointerException. The null object pattern uses a special object representing null, instead of using an actualnull. This allows you to invoke field and method references on the null object. The result of using the null object should semantically be equivalent todoing nothing.

Simple Example

Suppose we have the following system:

class Job {    def salary}class Person {    def name    def Job job}def people = [    new Person(name: 'Tom', job: new Job(salary: 1000)),    new Person(name: 'Dick', job: new Job(salary: 1200)),]def biggestSalary = people.collect { p -> p.job.salary }.max()println biggestSalary

When run, this prints out1200. Suppose now that we now invoke:

people << new Person(name: 'Harry')

If we now try to calculatebiggestSalary again, we receive a null pointer exception.

To overcome this problem, we can introduce aNullJob class and change the above statement to become:

class NullJob extends Job { def salary = 0 }people << new Person(name: 'Harry', job: new NullJob())biggestSalary = people.collect { p -> p.job.salary }.max()println biggestSalary

This works as we require but it’s not always the best way to do this with Groovy. Groovy’s safe-dereference operator (?.) operator and null aware closures often allow Groovy to avoid the need to create a special null object or null class. This is illustrated by examining a groovier way to write the above example:

people << new Person(name:'Harry')biggestSalary = people.collect { p -> p.job?.salary }.max()println biggestSalary

Two things are going on here to allow this to work. First of all,max() is'null aware' so that [300, null, 400].max() == 400. Secondly, with the?. operator, an expression likep?.job?.salary will be equal to null ifsalary is equal to null, or ifjob is equal ` null or ifp is equal to null. You don’t need to code a complex nested if ... then ... else to avoid aNullPointerException.

Tree Example

Consider the following example where we want to calculate size, cumulative sum and cumulative product of all the values in a tree structure.

Our first attempt has special logic within the calculation methods to handle null values.

class NullHandlingTree {    def left, right, value    def size() {        1 + (left ? left.size() : 0) + (right ? right.size() : 0)    }    def sum() {       value + (left ? left.sum() : 0) + (right ? right.sum() : 0)    }    def product() {       value * (left ? left.product() : 1) * (right ? right.product() : 1)    }}def root = new NullHandlingTree(    value: 2,    left: new NullHandlingTree(        value: 3,        right: new NullHandlingTree(value: 4),        left: new NullHandlingTree(value: 5)    ))println root.size()println root.sum()println root.product()

If we introduce the null object pattern (here by defining theNullTree class), we can now simplify the logic in thesize(),sum() and`product()` methods. These methods now much more clearly represent the logic for the normal (and now universal) case. Each of the methods withinNullTree returns a value which represents doing nothing.

class Tree {    def left = new NullTree(), right = new NullTree(), value    def size() {        1 + left.size() + right.size()    }    def sum() {       value + left.sum() + right.sum()    }    def product() {       value * left.product() * right.product()    }}class NullTree {    def size() { 0 }    def sum() { 0 }    def product() { 1 }}def root = new Tree(    value: 2,    left: new Tree(        value: 3,        right: new Tree(value: 4),        left: new Tree(value: 5)    ))println root.size()println root.sum()println root.product()

The result of running either of these examples is:

414120

Note: a slight variation with the null object pattern is to combine it with the singleton pattern. So, we wouldn’t write new NullTree() wherever we needed a null object as shown above. Instead we would have a single null object instance which we would place within our data structures as needed.

Observer Pattern

TheObserver Pattern allows one or moreobservers to be notifiedabout changes or events from asubject object.

ObserverClasses
Example

Here is a typical implementation of the classic pattern:

interface Observer {    void update(message)}class Subject {    private List observers = []    void register(observer) {        observers << observer    }    void unregister(observer) {        observers -= observer    }    void notifyAll(message) {        observers.each{ it.update(message) }    }}class ConcreteObserver1 implements Observer {    def messages = []    void update(message) {        messages << message    }}class ConcreteObserver2 implements Observer {    def messages = []    void update(message) {        messages << message.toUpperCase()    }}def o1a = new ConcreteObserver1()def o1b = new ConcreteObserver1()def o2 = new ConcreteObserver2()def observers = [o1a, o1b, o2]new Subject().with {    register(o1a)    register(o2)    notifyAll('one')}new Subject().with {    register(o1b)    register(o2)    notifyAll('two')}def expected = [['one'], ['two'], ['ONE', 'TWO']]assert observers*.messages == expected

Using Closures, we can avoid creating the concrete observer classes as shown below:

interface Observer {    void update(message)}class Subject {    private List observers = []    void register(Observer observer) {        observers << observer    }    void unregister(observer) {        observers -= observer    }    void notifyAll(message) {        observers.each{ it.update(message) }    }}def messages1a = [], messages1b = [], messages2 = []def o2 = { messages2 << it.toUpperCase() }new Subject().with {    register{ messages1a << it }    register(o2)    notifyAll('one')}new Subject().with {    register{ messages1b << it }    register(o2)    notifyAll('two')}def expected = [['one'], ['two'], ['ONE', 'TWO']]assert [messages1a, messages1b, messages2] == expected

As a variation for Groovy 3+, let’s consider dropping theObserver interface and using lambdas as shown below:

import java.util.function.Consumerclass Subject {    private List<Consumer> observers = []    void register(Consumer observer) {        observers << observer    }    void unregister(observer) {        observers -= observer    }    void notifyAll(message) {        observers.each{ it.accept(message) }    }}def messages1a = [], messages1b = [], messages2 = []def o2 = { messages2 << it.toUpperCase() }new Subject().with {    register(s -> messages1a << s)    register(s -> messages2 << s.toUpperCase())    notifyAll('one')}new Subject().with {    register(s -> messages1b << s)    register(s -> messages2 << s.toUpperCase())    notifyAll('two')}def expected = [['one'], ['two'], ['ONE', 'TWO']]assert [messages1a, messages1b, messages2] == expected

We are now calling theaccept method fromConsumer ratherthan theupdate method fromObserver.

@Bindable and @Vetoable

The JDK has some built-in classes which follow the observer pattern.Thejava.util.Observer andjava.util.Observable classes are deprecated from JDK 9 due to various limitations.Instead, you are recommended to use various more powerful classes in thejava.beans package such asjava.beans.PropertyChangeListener.Luckily, Groovy has some built-in transforms (groovy.beans.Bindable andgroovy.beans.Vetoable)which support for some key classes from that package.

import groovy.beans.*import java.beans.*class PersonBean {    @Bindable String first    @Bindable String last    @Vetoable Integer age}def messages = [:].withDefault{[]}new PersonBean().with {    addPropertyChangeListener{ PropertyChangeEvent ev ->        messages[ev.propertyName] << "prop: $ev.newValue"    }    addVetoableChangeListener{ PropertyChangeEvent ev ->        def name = ev.propertyName        if (name == 'age' && ev.newValue > 40)            throw new PropertyVetoException()        messages[name] << "veto: $ev.newValue"    }    first = 'John'    age = 35    last = 'Smith'    first = 'Jane'    age = 42}def expected = [    first:['prop: John', 'prop: Jane'],    age:['veto: 35'],    last:['prop: Smith']]assert messages == expected

Here, methods likeaddPropertyChangeListener perform the same role asregisterObserver in previous examples.There is afirePropertyChange method corresponding tonotifyAll/notifyObservers in previous examples but Groovy adds thatautomatically here, so it isn’t visible in the source code. There is also apropertyChange method that correspondsto theupdate method in previous examples, though again, that isn’t visible here.

Pimp my Library Pattern

ThePimp my Library Pattern suggests an approach for extending a library that nearly does everything that you need but just needs a little more. It assumes that you do not have source code for the library of interest.

Example

Suppose we want to make use of the built-in Integer facilities in Groovy (which build upon the features already in Java). Those libraries have nearly all of the features we want but not quite everything. We may not have all of the source code to the Groovy and Java libraries so we can’t just change the library. Instead we augment the library. Groovy has a number of ways to do this. One way is to use a Category.

First, we’ll define a suitable category.

class EnhancedInteger {    static boolean greaterThanAll(Integer self, Object[] others) {        greaterThanAll(self, others)    }    static boolean greaterThanAll(Integer self, others) {        others.every { self > it }    }}

We have added two methods which augment the Integer methods by providing thegreaterThanAll method. Categories follow conventions where they are defined as static methods with a special first parameter representing the class we wish to extend. The greaterThanAll(Integer self, others) static method becomes thegreaterThanAll(other) instance method.

We defined two versions ofgreaterThanAll. One which works for collections, ranges etc. The other which works with a variable number ofInteger arguments.

Here is how you would use the category.

use(EnhancedInteger) {    assert 4.greaterThanAll(1, 2, 3)    assert !5.greaterThanAll(2, 4, 6)    assert 5.greaterThanAll(-4..4)    assert 5.greaterThanAll([])    assert !5.greaterThanAll([4, 5])}

As you can see, using this technique you can effectively enrich an original class without having access to its source code. Moreover, you can apply different enrichments in different parts of the system as well as work with un-enriched objects if we need to.

Proxy Pattern

TheProxy Pattern allows one object to act as a pretend replacement for some other object. In general, whoever is using the proxy, doesn’t realise that they are not using the real thing. The pattern is useful when the real object is hard to create or use: it may exist over a network connection, or be a large object in memory, or be a file, database or some other resource that is expensive or impossible to duplicate.

Example

One common use of the proxy pattern is when talking to remote objects in a different JVM. Here is the client code for creating a proxy that talks via sockets to a server object as well as an example usage:

class AccumulatorProxy {    def accumulate(args) {        def result        def s = new Socket("localhost", 54321)        s.withObjectStreams { ois, oos ->            oos << args            result = ois.readObject()        }        s.close()        return result    }}println new AccumulatorProxy().accumulate([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])// => 55

Here is what your server code might look like (start this first):

class Accumulator {    def accumulate(args) {        args.inject(0) { total, arg -> total += arg }    }}def port = 54321def accumulator = new Accumulator()def server = new ServerSocket(port)println "Starting server on port $port"while(true) {    server.accept() { socket ->        socket.withObjectStreams { ois, oos ->            def args = ois.readObject()            oos << accumulator.accumulate(args)        }    }}
Singleton Pattern

TheSingleton Pattern is used to make sure only one object of a particular class is ever created. This can be useful when exactly one object is needed to coordinate actions across a system; perhaps for efficiency where creating lots of identical objects would be wasteful, perhaps because a particular algorithm needing a single point of control is required or perhaps when an object is used to interact with a non-shareable resource.

Weaknesses of the Singleton pattern include:

  • It can reduce reuse. For instance, there are issues if you want to use inheritance with Singletons. IfSingletonB extendsSingletonA, should there be exactly (at most) one instance of each or should the creation of an object from one of the classes prohibit creation from the other. Also, if you decide both classes can have an instance, how do you override thegetInstance() method which is static?

  • It is also hard to test singletons in general because of the static methods but Groovy can support that if required.

Example: The Classic Java Singleton

Suppose we wish to create a class for collecting votes. Because getting the right number of votes may be very important, we decide to use the singleton pattern. There will only ever be oneVoteCollector object, so it makes it easier for us to reason about that objects creation and use.

class VoteCollector {    def votes = 0    private static final INSTANCE = new VoteCollector()    static getInstance() { return INSTANCE }    private VoteCollector() { }    def display() { println "Collector:${hashCode()}, Votes:$votes" }}

Some points of interest in this code:

  • it has a private constructor, so noVoteCollector objects can be created in our system (except for theINSTANCE we create)

  • theINSTANCE is also private, so it can’t be changed once set

  • we haven’t made the updating of votes thread-safe at this point (it doesn’t add to this example)

  • the vote collector instance is not lazily created (if we never reference the class, the instance won’t be created; however, as soon as we reference the class, the instance will be created even if not needed initially)

We can use this singleton class in some script code as follows:

def collector = VoteCollector.instancecollector.display()collector.votes++collector = nullThread.start{    def collector2 = VoteCollector.instance    collector2.display()    collector2.votes++    collector2 = null}.join()def collector3 = VoteCollector.instancecollector3.display()

Here we used the instance 3 times. The second usage was even in a different thread (but don’t try this in a scenario with a new class loader).

Running this script yields (your hashcode value will vary):

Collector:15959960, Votes:0Collector:15959960, Votes:1Collector:15959960, Votes:2

Variations to this pattern:

  • To support lazy-loading and multi-threading, we could just use thesynchronized keyword with thegetInstance() method. This has a performance hit but will work.

  • We can consider variations involving double-checked locking and thevolatile keyword, but see the limitations of this approachhere.

Example: Singleton via MetaProgramming

Groovy’s meta-programming capabilities allow concepts like the singleton pattern to be enacted in a far more fundamental way. This example illustrates a simple way to use Groovy’s meta-programming capabilities to achieve the singleton pattern but not necessarily the most efficient way.

Suppose we want to keep track of the total number of calculations that a calculator performs. One way to do that is to use a singleton for the calculator class and keep a variable in the class with the count.

First we define some base classes. ACalculator class which performs calculations and records how many such calculations it performs and aClient class which acts as a facade to the calculator.

class Calculator {    private total = 0    def add(a, b) { total++; a + b }    def getTotalCalculations() { 'Total Calculations: ' + total }    String toString() { 'Calc: ' + hashCode() }}class Client {    def calc = new Calculator()    def executeCalc(a, b) { calc.add(a, b) }    String toString() { 'Client: ' + hashCode() }}

Now we can define and register aMetaClass which intercepts all attempts to create aCalculator object and always provides a pre-created instance instead. We also register this MetaClass with the Groovy system:

class CalculatorMetaClass extends MetaClassImpl {    private static final INSTANCE = new Calculator()    CalculatorMetaClass() { super(Calculator) }    def invokeConstructor(Object[] arguments) { return INSTANCE }}def registry = GroovySystem.metaClassRegistryregistry.setMetaClass(Calculator, new CalculatorMetaClass())

Now we use instances of ourClient class from within a script. The client class will attempt to create new instances of the calculator but will always get the singleton.

def client = new Client()assert 3 == client.executeCalc(1, 2)println "$client, $client.calc, $client.calc.totalCalculations"client = new Client()assert 4 == client.executeCalc(2, 2)println "$client, $client.calc, $client.calc.totalCalculations"

Here is the result of running this script (your hashcode values may vary):

Client: 7306473, Calc: 24230857, Total Calculations: 1Client: 31436753, Calc: 24230857, Total Calculations: 2
Guice Example

We can also implement the Singleton Pattern usingGuice.

Consider the Calculator example again.

Guice is a Java-oriented framework that supports Interface-Oriented design. Hence we create aCalculator interface first. We can then create ourCalculatorImpl implementation and aClient object which our script will interact with. TheClient class isn’t strictly needed for this example but allows us to show that non-singleton instances are the default. Here is the code:

@Grapes([@Grab('aopalliance:aopalliance:1.0'), @Grab('com.google.code.guice:guice:1.0')])import com.google.inject.*interface Calculator {    def add(a, b)}class CalculatorImpl implements Calculator {    private total = 0    def add(a, b) { total++; a + b }    def getTotalCalculations() { 'Total Calculations: ' + total }    String toString() { 'Calc: ' + hashCode() }}class Client {    @Inject Calculator calc    def executeCalc(a, b) { calc.add(a, b) }    String toString() { 'Client: ' + hashCode() }}def injector = Guice.createInjector (    [configure: { binding ->        binding.bind(Calculator)               .to(CalculatorImpl)               .asEagerSingleton() } ] as Module)def client = injector.getInstance(Client)assert 3 == client.executeCalc(1, 2)println "$client, $client.calc, $client.calc.totalCalculations"client = injector.getInstance(Client)assert 4 == client.executeCalc(2, 2)println "$client, $client.calc, $client.calc.totalCalculations"

Note the@Inject annotation in theClient class. We can always tell right in the source code which fields will be injected.

In this example we chose to use anexplicit binding. All of our dependencies (ok, only one in this example at the moment) are configured in the binding. The Guide injector knows about the binding and injects the dependencies as required when we create objects. For the singleton pattern to hold, you must always use Guice to create your instances. Nothing shown so far would stop you creating another instance of the calculator manually using new CalculatorImpl() which would of course violate the desired singleton behaviour.

In other scenarios (though probably not in large systems), we could choose to express dependencies using annotations, such as the following example shows:

@Grapes([@Grab('aopalliance:aopalliance:1.0'), @Grab('com.google.code.guice:guice:1.0')])import com.google.inject.*@ImplementedBy(CalculatorImpl)interface Calculator {    // as before ...}@Singletonclass CalculatorImpl implements Calculator {    // as before ...}class Client {    // as before ...}def injector = Guice.createInjector()// ...

Note the@Singleton annotation on theCalculatorImpl class and the@ImplementedBy annotation in theCalculator interface.

When run, the above example (using either approach) yields (your hashcode values will vary):

Client: 8897128, Calc: 17431955, Total Calculations: 1Client: 21145613, Calc: 17431955, Total Calculations: 2

You can see that we obtained a new client object whenever we asked for an instance but it was injected with the same calculator object.

Spring Example

We can do the Calculator example again using Spring as follows:

@Grapes([@Grab('org.springframework:spring-core:5.2.8.RELEASE'), @Grab('org.springframework:spring-beans:5.2.8.RELEASE')])import org.springframework.beans.factory.support.*interface Calculator {    def add(a, b)}class CalculatorImpl implements Calculator {    private total = 0    def add(a, b) { total++; a + b }    def getTotalCalculations() { 'Total Calculations: ' + total }    String toString() { 'Calc: ' + hashCode() }}class Client {    Client(Calculator calc) { this.calc = calc }    def calc    def executeCalc(a, b) { calc.add(a, b) }    String toString() { 'Client: ' + hashCode() }}// Here we 'wire' up our dependencies through the API. Alternatively,// we could use XML-based configuration or the Grails Bean Builder DSL.def factory = new DefaultListableBeanFactory()factory.registerBeanDefinition('calc', new RootBeanDefinition(CalculatorImpl))def beanDef = new RootBeanDefinition(Client, false)beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_AUTODETECT)factory.registerBeanDefinition('client', beanDef)def client = factory.getBean('client')assert 3 == client.executeCalc(1, 2)println "$client, $client.calc, $client.calc.totalCalculations"client = factory.getBean('client')assert 4 == client.executeCalc(2, 2)println "$client, $client.calc, $client.calc.totalCalculations"

And here is the result (your hashcode values will vary):

Client: 29418586, Calc: 10580099, Total Calculations: 1Client: 14800362, Calc: 10580099, Total Calculations: 2
State Pattern

TheState Pattern provides a structured approach to partitioning the behaviour within complex systems. The overall behaviour of a system is partitioned into well-defined states. Typically, each state is implemented by a class. The overall system behaviour can be determined firstly by knowing thecurrent state of the system; secondly, by understanding the behaviour possible while in that state (as embodied in the methods of the class corresponding to that state).

Example

Here is an example:

class Client {    def context = new Context()    def connect() {        context.state.connect()    }    def disconnect() {        context.state.disconnect()    }    def send_message(message) {        context.state.send_message(message)    }    def receive_message() {        context.state.receive_message()    }}class Context {    def state = new Offline(this)}class ClientState {    def context    ClientState(context) {        this.context = context        inform()    }}class Offline extends ClientState {    Offline(context) {        super(context)    }    def inform() {        println "offline"    }    def connect() {        context.state = new Online(context)    }    def disconnect() {        println "error: not connected"    }    def send_message(message) {        println "error: not connected"    }    def receive_message() {        println "error: not connected"    }}class Online extends ClientState {    Online(context) {        super(context)    }    def inform() {        println "connected"    }    def connect() {        println "error: already connected"    }    def disconnect() {        context.state = new Offline(context)    }    def send_message(message) {        println "\"$message\" sent"    }    def receive_message() {        println "message received"    }}client = new Client()client.send_message("Hello")client.connect()client.send_message("Hello")client.connect()client.receive_message()client.disconnect()

Here is the output:

offlineerror: not connectedconnected"Hello" senterror: already connectedmessage receivedoffline

One of the great things about a dynamic language like Groovy though is that we can take this example and express it in many different ways depending on our particular needs. Some potential variations for this example are shown below.

Variation 1: Leveraging Interface-Oriented Design

One approach we could take is to leverageInterface-Oriented Design. To do this, we could introduce the following interface:

interface State {    def connect()    def disconnect()    def send_message(message)    def receive_message()}

Then ourClient,Online and 'Offline` classes could be modified to implement that interface, e.g.:

class Client implements State {  // ... as before ...}class Online implements State {  // ... as before ...}class Offline implements State {  // ... as before ...}

You might ask: Haven’t we just introduced additional boilerplate code? Can’t we rely on duck-typing for this? The answer is 'yes' and 'no'. We can get away with duck-typing but one of the key intentions of the state pattern is to partition complexity. If we know that theclient class and eachstate class all satisfy one interface, then we have placed some key boundaries around the complexity. We can look at any state class in isolation and know the bounds of behaviour possible for that state.

We don’t have to use interfaces for this, but it helps express the intent of this particular style of partitioning and it helps reduce the size of our unit tests (we would have to have additional tests in place to express this intent in languages which have less support for interface-oriented design).

Variation 2: Extract State Pattern Logic

Alternatively, or in combination with other variations, we might decide to extract some of our State Pattern logic into helper classes. For example, we could define the following classes in a state pattern package/jar/script:

abstract class InstanceProvider {    static def registry = GroovySystem.metaClassRegistry    static def create(objectClass, param) {        registry.getMetaClass(objectClass).invokeConstructor([param] as Object[])    }}abstract class Context {    private context    protected setContext(context) {        this.context = context    }    def invokeMethod(String name, Object arg) {        context.invokeMethod(name, arg)    }    def startFrom(initialState) {        setContext(InstanceProvider.create(initialState, this))    }}abstract class State {    private client    State(client) { this.client = client }    def transitionTo(nextState) {        client.setContext(InstanceProvider.create(nextState, client))    }}

This is all quite generic and can be used wherever we want to introduce the state pattern. Here is what our code would look like now:

class Client extends Context {    Client() {        startFrom(Offline)    }}class Offline extends State {    Offline(client) {        super(client)        println "offline"    }    def connect() {        transitionTo(Online)    }    def disconnect() {        println "error: not connected"    }    def send_message(message) {        println "error: not connected"    }    def receive_message() {        println "error: not connected"    }}class Online extends State {    Online(client) {        super(client)        println "connected"    }    def connect() {        println "error: already connected"    }    def disconnect() {        transitionTo(Offline)    }    def send_message(message) {        println "\"$message\" sent"    }    def receive_message() {        println "message received"    }}client = new Client()client.send_message("Hello")client.connect()client.send_message("Hello")client.connect()client.receive_message()client.disconnect()

You can see here thestartFrom andtransitionTo methods begin to give our example code a DSL feel.

Variation 3: Bring on the DSL

Alternatively, or in combination with other variations, we might decide to fully embrace a Domain Specific Language (DSL) approach to this example.

We can define the following generic helper functions (first discussedhere):

class Grammar {    def fsm    def event    def fromState    def toState    Grammar(a_fsm) {        fsm = a_fsm    }    def on(a_event) {        event = a_event        this    }    def on(a_event, a_transitioner) {        on(a_event)        a_transitioner.delegate = this        a_transitioner.call()        this    }    def from(a_fromState) {        fromState = a_fromState        this    }    def to(a_toState) {        assert a_toState, "Invalid toState: $a_toState"        toState = a_toState        fsm.registerTransition(this)        this    }    def isValid() {        event && fromState && toState    }    public String toString() {        "$event: $fromState=>$toState"    }}
class FiniteStateMachine {    def transitions = [:]    def initialState    def currentState    FiniteStateMachine(a_initialState) {        assert a_initialState, "You need to provide an initial state"        initialState = a_initialState        currentState = a_initialState    }    def record() {        Grammar.newInstance(this)    }    def reset() {        currentState = initialState    }    def isState(a_state) {        currentState == a_state    }    def registerTransition(a_grammar) {        assert a_grammar.isValid(), "Invalid transition ($a_grammar)"        def transition        def event = a_grammar.event        def fromState = a_grammar.fromState        def toState = a_grammar.toState        if (!transitions[event]) {            transitions[event] = [:]        }        transition = transitions[event]        assert !transition[fromState], "Duplicate fromState $fromState for transition $a_grammar"        transition[fromState] = toState    }    def fire(a_event) {        assert currentState, "Invalid current state '$currentState': passed into constructor"        assert transitions.containsKey(a_event), "Invalid event '$a_event', should be one of ${transitions.keySet()}"        def transition = transitions[a_event]        def nextState = transition[currentState]        assert nextState, "There is no transition from '$currentState' to any other state"        currentState = nextState        currentState    }}

Now we can define and test our state machine like this:

class StatePatternDslTest extends GroovyTestCase {    private fsm    protected void setUp() {        fsm = FiniteStateMachine.newInstance('offline')        def recorder = fsm.record()        recorder.on('connect').from('offline').to('online')        recorder.on('disconnect').from('online').to('offline')        recorder.on('send_message').from('online').to('online')        recorder.on('receive_message').from('online').to('online')    }    void testInitialState() {        assert fsm.isState('offline')    }    void testOfflineState() {        shouldFail{            fsm.fire('send_message')        }        shouldFail{            fsm.fire('receive_message')        }        shouldFail{            fsm.fire('disconnect')        }        assert 'online' == fsm.fire('connect')    }    void testOnlineState() {        fsm.fire('connect')        fsm.fire('send_message')        fsm.fire('receive_message')        shouldFail{            fsm.fire('connect')        }        assert 'offline' == fsm.fire('disconnect')    }}

This example isn’t an exact equivalent of the others. It doesn’t use predefinedOnline andOffline classes.Instead, it defines the entire state machine on the fly as needed.See theprevious reference for more elaborate examples of this style.

Strategy Pattern

TheStrategy Pattern allows you to abstract away particular algorithms from their usage. This allows you to easily swap the algorithm being used without having to change the calling code. The general form of the pattern is:

StrategyClasses

In Groovy, because of its ability to treat code as a first class object using anonymous methods (which we loosely callClosures), the need for the strategy pattern is greatly reduced. You can simply place algorithms inside Closures.

Example using traditional class hierarchy

First let’s look at the traditional way of encapsulating the Strategy Pattern.

interface Calc {    def execute(n, m)}class CalcByMult implements Calc {    def execute(n, m) { n * m }}class CalcByManyAdds implements Calc {    def execute(n, m) {        def result = 0        n.times{            result += m        }        result    }}def sampleData = [    [3, 4, 12],    [5, -5, -25]]Calc[] multiplicationStrategies = [    new CalcByMult(),    new CalcByManyAdds()]sampleData.each{ data ->    multiplicationStrategies.each { calc ->        assert data[2] == calc.execute(data[0], data[1])    }}

Here we have defined an interfaceCalc which our concrete strategy classes will implement (we could also have used an abstract class).We then defined two algorithms for doing simple multiplication:CalcByMult the normal way, and CalcByManyAdds using only addition (don’t try this one using negative numbers - yes we could fix this but it would just make the example longer).We then use normalpolymorphism to invoke the algorithms.

Example using closures

Here is the Groovier way to achieve the same thing using Closures:

def multiplicationStrategies = [    { n, m -> n * m },    { n, m -> def result = 0; n.times{ result += m }; result }]def sampleData = [    [3, 4, 12],    [5, -5, -25]]sampleData.each{ data ->    multiplicationStrategies.each { calc ->        assert data[2] == calc(data[0], data[1])    }}
Example using lambdas

For Groovy 3+, we can leverage lambda syntax:

interface Calc {    def execute(n, m)}List<Calc> multiplicationStrategies = [    (n, m) -> n * m,    (n, m) -> { def result = 0; n.times{ result += m }; result }]def sampleData = [    [3, 4, 12],    [5, -5, -25]]sampleData.each{ data ->    multiplicationStrategies.each { calc ->        assert data[2] == calc(data[0], data[1])    }}

Or we can use the built-in JDKBiFunction class:

import java.util.function.BiFunctionList<BiFunction<Integer, Integer, Integer>> multiplicationStrategies = [    (n, m) -> n * m,    (n, m) -> { def result = 0; n.times{ result += m }; result }]def sampleData = [    [3, 4, 12],    [5, -5, -25]]sampleData.each{ data ->    multiplicationStrategies.each { calc ->        assert data[2] == calc(data[0], data[1])    }}
Template Method Pattern

TheTemplate Method Pattern abstractsaway the details of several algorithms.The generic part of an algorithm is contained within a base class.Particular implementation details are captured within child classes.The generic pattern of classes involved looks like this:

TemplateMethodClasses
Example with traditional classes

In this example, the baseAccumulator class captures the essence of the accumulation algorithm.The child classesSum andProduct provide particular customised ways to use the generic accumulation algorithm.

abstract class Accumulator {    protected initial    abstract doAccumulate(total, v)    def accumulate(values) {        def total = initial        values.each { v -> total = doAccumulate(total, v) }        total    }}class Sum extends Accumulator {    def Sum() { initial = 0 }    def doAccumulate(total, v) { total + v }}class Product extends Accumulator {    def Product() { initial = 1 }    def doAccumulate(total, v) { total * v }}assert 10 == new Sum().accumulate([1,2,3,4])assert 24 == new Product().accumulate([1,2,3,4])
Example with simplifying strategies

In this particular case, you could use Groovy’s inject method to achieve a similar result using Closures:

Closure addAll = { total, item -> total += item }def accumulated = [1, 2, 3, 4].inject(0, addAll)assert accumulated == 10

Thanks to duck-typing, this would also work with other objects which support an add (plus() in Groovy) method, e.g.:

accumulated = [ "1", "2", "3", "4" ].inject("", addAll)assert accumulated == "1234"

We could also do the multiplication case as follows (re-writing as a one-liner):

assert 24 == [1, 2, 3, 4].inject(1) { total, item -> total *= item }

Using closures this way looks like theStrategy Pattern, but if we realisethat Groovy’sinject method is the generic part of the algorithm for our template method,then the Closures become the customised parts of the template method pattern.

For Groovy 3+, we can use lambda syntax as an alternative to the closure syntax:

assert 10 == [1, 2, 3, 4].stream().reduce(0, (l, r) -> l + r)assert 24 == [1, 2, 3, 4].stream().reduce(1, (l, r) -> l * r)assert '1234' == ['1', '2', '3', '4'].stream().reduce('', (l, r) -> l + r)

Here the stream api’sreduce method is the generic part of the algorithm for our template method,and the lambdas are the customised parts of the template method pattern.

Visitor Pattern

TheVisitor Pattern is one of those well-known but notoften used patterns. Perhaps this is because it seems a little complex at first.But once you become familiar with it, it becomes a powerful way to evolve your codeand as we’ll see, Groovy provides ways to reduce some to the complexity, so there isno reason not to consider using this pattern.

The goal of the pattern is to separate an algorithm from an object structure.A practical result of this separation is the ability to add new operations to existingobject structures without modifying those structures.

Simple Example

This example considers how to calculate the bounds of shapes (or collections of shapes).Our first attempt uses the traditional visitor pattern.We will see a more Groovy way to do this shortly.

abstract class Shape { }@ToString(includeNames=true)class Rectangle extends Shape {    def x, y, w, h    Rectangle(x, y, w, h) {        this.x = x; this.y = y; this.w = w; this.h = h    }    def union(rect) {        if (!rect) return this        def minx = [rect.x, x].min()        def maxx = [rect.x + rect.w, x + w].max()        def miny = [rect.y, y].min()        def maxy = [rect.y + rect.h, y + h].max()        new Rectangle(minx, miny, maxx - minx, maxy - miny)    }    def accept(visitor) {        visitor.visit_rectangle(this)    }}class Line extends Shape {    def x1, y1, x2, y2    Line(x1, y1, x2, y2) {        this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2    }    def accept(visitor){        visitor.visit_line(this)    }}class Group extends Shape {    def shapes = []    def add(shape) { shapes += shape }    def remove(shape) { shapes -= shape }    def accept(visitor) {        visitor.visit_group(this)    }}class BoundingRectangleVisitor {    def bounds    def visit_rectangle(rectangle) {        if (bounds)            bounds = bounds.union(rectangle)        else            bounds = rectangle    }    def visit_line(line) {        def line_bounds = new Rectangle([line.x1, line.x2].min(),                                        [line.y1, line.y2].min(),                                        line.x2 - line.y1,                                        line.x2 - line.y2)        if (bounds)            bounds = bounds.union(line_bounds)        else            bounds = line_bounds    }    def visit_group(group) {        group.shapes.each { shape -> shape.accept(this) }    }}def group = new Group()group.add(new Rectangle(100, 40, 10, 5))group.add(new Rectangle(100, 70, 10, 5))group.add(new Line(90, 30, 60, 5))def visitor = new BoundingRectangleVisitor()group.accept(visitor)bounding_box = visitor.boundsassert bounding_box.toString() == 'Rectangle(x:60, y:5, w:50, h:70)'

That took quite a bit of code, but the idea now is that we could addfurther algorithms just by adding new visitors with our shape classes remainingunchanged, e.g. we could add a total area visitor or a collision detection visitor.

We can improve the clarity of our code (and shrink it to about half the size) bymaking use of Groovy Closures as follows:

abstract class Shape {    def accept(Closure yield) { yield(this) }}@ToString(includeNames=true)class Rectangle extends Shape {    def x, y, w, h    def bounds() { this }    def union(rect) {        if (!rect) return this        def minx = [ rect.x, x ].min()        def maxx = [ rect.x + rect.w, x + w ].max()        def miny = [ rect.y, y ].min()        def maxy = [ rect.y + rect.h, y + h ].max()        new Rectangle(x:minx, y:miny, w:maxx - minx, h:maxy - miny)    }}class Line extends Shape {    def x1, y1, x2, y2    def bounds() {        new Rectangle(x:[x1, x2].min(), y:[y1, y2].min(),                      w:(x2 - x1).abs(), h:(y2 - y1).abs())    }}class Group {    def shapes = []    def leftShift(shape) { shapes += shape }    def accept(Closure yield) { shapes.each{it.accept(yield)} }}def group = new Group()group << new Rectangle(x:100, y:40, w:10, h:5)group << new Rectangle(x:100, y:70, w:10, h:5)group << new Line(x1:90, y1:30, x2:60, y2:5)def boundsgroup.accept{ bounds = it.bounds().union(bounds) }assert bounds.toString() == 'Rectangle(x:60, y:5, w:50, h:70)'

Or, using lambdas as follows:

abstract class Shape {    def accept(Function<Shape, Shape> yield) { yield.apply(this) }}@ToString(includeNames=true)class Rectangle extends Shape {    /* ... same as with Closures ... */}class Line extends Shape {    /* ... same as with Closures ... */}class Group {    def shapes = []    def leftShift(shape) { shapes += shape }    def accept(Function<Shape, Shape> yield) {        shapes.stream().forEach(s -> s.accept(yield))    }}def group = new Group()group << new Rectangle(x:100, y:40, w:10, h:5)group << new Rectangle(x:100, y:70, w:10, h:5)group << new Line(x1:90, y1:30, x2:60, y2:5)def boundsgroup.accept(s -> { bounds = s.bounds().union(bounds) })assert bounds.toString() == 'Rectangle(x:60, y:5, w:50, h:70)'
Advanced Example

Let’s consider another example to illustrate some more points about this pattern.

interface Visitor {    void visit(NodeType1 n1)    void visit(NodeType2 n2)}interface Visitable {    void accept(Visitor visitor)}class NodeType1 implements Visitable {    Visitable[] children = new Visitable[0]    void accept(Visitor visitor) {        visitor.visit(this)        for(int i = 0; i < children.length; ++i) {            children[i].accept(visitor)        }    }}class NodeType2 implements Visitable {    Visitable[] children = new Visitable[0]    void accept(Visitor visitor) {        visitor.visit(this)        for(int i = 0; i < children.length; ++i) {            children[i].accept(visitor)        }    }}class NodeType1Counter implements Visitor {    int count = 0    void visit(NodeType1 n1) {        count++    }    void visit(NodeType2 n2){}}

If we now useNodeType1Counter on a tree like this:

NodeType1 root = new NodeType1()root.children = new Visitable[]{new NodeType1(), new NodeType2()}def counter = new NodeType1Counter()root.accept(counter)assert counter.count == 2

Then we have oneNodeType1 object as root and one of the children is also aNodeType1 instance.The other child is aNodeType2 instance.That means usingNodeType1Counter here should count 2NodeType1 objects as the last statement verifies.

When to use this

This example illustrates some of the advantages of the visitor pattern.For example, while our visitor has state (the count ofNodeType1 objects), the tree of objects itself is not changed.Similarly, if we wanted to have a visitor counting all node types,or one that counts how many different types are used, or one that gathers informationusing methods special to the node types, again, the visitor alone is all that would need to be written.

What happens if we add a new type?

In this case we might have a fair bit of work to do. We probably have to change theVisitor interface to accept the new type,and change potentially most existing visitors based on that interface change,and we have to write the new type itself.A better approach is to write a default implementation of the visitor which all concrete visitors will extend.We’ll see this approach in use shortly.

What if we want to have different iteration patterns?

Then you have a problem.Since the node describes how to iterate, you have no influence and stop iteration at a point or change the order.So maybe we should change this a little to this:

interface Visitor {    void visit(NodeType1 n1)    void visit(NodeType2 n2)}class DefaultVisitor implements Visitor{    void visit(NodeType1 n1) {        for(int i = 0; i < n1.children.length; ++i) {            n1.children[i].accept(this)        }    }    void visit(NodeType2 n2) {        for(int i = 0; i < n2.children.length; ++i) {            n2.children[i].accept(this)        }    }}interface Visitable {    void accept(Visitor visitor)}class NodeType1 implements Visitable {    Visitable[] children = new Visitable[0]    void accept(Visitor visitor) {        visitor.visit(this)    }}class NodeType2 implements Visitable {    Visitable[] children = new Visitable[0];    void accept(Visitor visitor) {        visitor.visit(this)    }}class NodeType1Counter extends DefaultVisitor {    int count = 0    void visit(NodeType1 n1) {        count++        super.visit(n1)    }}

Some small changes but with big effect.The visitor is now recursive and tells me how to iterate.The implementation in the Nodes is minimized tovisitor.visit(this),DefaultVisitor is now able to catch the new types, we can stop iteration by not delegating to super.Of course the big disadvantage now is that it is no longer iterative, but you can’t get all the benefits.

Make it Groovy

The question now is how to make that a bit more Groovy.Didn’t you find thisvisitor.visit(this) strange? Why is it there?The answer is to simulate double dispatch.In Java, the compile time type is used, so forvisitor.visit(children[i]) the compiler won’t beable to find the correct method, becauseVisitor does not contain a methodvisit(Visitable).And even if it would, we would like to visit the more special methods withNodeType1 orNodeType2.

Now Groovy is not using the static type, Groovy uses the runtime type.This means we can usevisitor.visit(children[i]) without any problem.Since we minimized the accept method to just do the double dispatch part andsince the runtime type system of Groovy will already cover that, do we need the accept method?Not really, but we can do even more.We had the disadvantage of not knowing how to handle unknown tree elements.We had toextend the interfaceVisitor for that, resulting in changes toDefaultVisitor andthen we have the task to provide a useful default like iterating the node or not doing anything at all.Now with Groovy we can catch that case by adding avisit(Visitable) method that does nothing.That would be the same in Java btw.

But don’t let us stop here. Do we need theVisitor interface?If we don’t have the accept method, then we don’t need theVisitor interface at all.So the new code would be:

class DefaultVisitor {    void visit(NodeType1 n1) {        n1.children.each { visit(it) }    }    void visit(NodeType2 n2) {        n2.children.each { visit(it) }    }    void visit(Visitable v) { }}interface Visitable { }class NodeType1 implements Visitable {    Visitable[] children = []}class NodeType2 implements Visitable {    Visitable[] children = []}class NodeType1Counter extends DefaultVisitor {    int count = 0    void visit(NodeType1 n1) {        count++        super.visit(n1)    }}

Looks like we saved a few lines of code here, but we made more.TheVisitable nodes now do not refer to anyVisitor class or interface.This is about the best level of separation you might expect here, but we can go further.Let’s change theVisitable interface a little and let it return the children we want to visit next.This allows us a general iteration method.

class DefaultVisitor {    void visit(Visitable v) {        doIteration(v)    }    void doIteration(Visitable v) {        v.children.each {            visit(it)        }    }}interface Visitable {    Visitable[] getChildren()}class NodeType1 implements Visitable {    Visitable[] children = []}class NodeType2 implements Visitable {    Visitable[] children = []}class NodeType1Counter extends DefaultVisitor {    int count = 0    void visit(NodeType1 n1) {        count++        super.visit(n1)    }}

DefaultVisitor now looks a bit different.It has adoIteration method that will get the children it should iterate over and then call visit on each element.Per default this will callvisit(Visitable) which then iterates over the children of this child.Visitable has also changed to ensure that any node will be able to return children (even if empty).We didn’t have to change theNodeType1 andNodeType2 class, because the way the children field wasdefined already made them a property, which means Groovy is so nice to generate a get method for us.Now the really interesting part isNodeType1Counter, it is interesting because we have not changed it.super.visit(n1) will now callvisit(Visitable) which will calldoIteration which will start the next level of iteration.So no change. Butvisit(it) will callvisit(NodeType1) if it is of typeNodeType1.In fact, we don’t need thedoIteration method, we could do that invisit(Visitable) too,but this variant has some benefits. It allows us to write a newVisitor that overwrites visit(Visitable)for error cases which of course means we must not dosuper.visit(n1) butdoIteration(n1).

Summary

In the end we got ~40% less code, a robust and stable architecture,and we completely removed the Visitor from the Visitable.To achieve the same in Java, you would probably need to resort to reflection.

The visitor pattern has sometimes been described as a poor fit for extreme programmingtechniques because you need to make changes to so many classes all the time.With our design, if we add new types we don’t need to change anything.So, the pattern is a good fit for agile approaches when using Groovy.

There are variants of the Visitor pattern, like theacyclic visitor pattern,that try to solve the problem of adding new node types with special visitors.The implementations of these visitors have their own code smells, like using casts, overuse ofinstanceof, and other tricks.What’s more the problems such approaches are trying to solve don’t occur within the Groovy version. We recommend avoiding that variant of this pattern.

Finally, in case it isn’t obvious,NodeType1Counter could be implemented in Java as well.Groovy will recognize the visit methods and call them as needed becauseDefaultVisitor is still Groovy and does all the magic.

3.25.2. References

  1. Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1995).Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. ISBN 0-201-63361-2.

    • The canonical reference of design patterns.

  2. Martin Fowler (1999).Refactoring: Improving the Design of Existing Code. Addison-Wesley. ISBN 0-201-48567-2.

  3. Joshua Kerievsky (2004).Refactoring To Patterns. Addison-Wesley. ISBN 0-321-21335-1.

  4. Eric Freeman, Elisabeth Freeman, Kathy Sierra, Bert Bates (2004).Head First Design Patterns. O’Reilly. ISBN 0-596-00712-4.*A great book to read, informative as well as amusing.

  5. Dierk Koenig with Andrew Glover, Paul King, Guillaume Laforge and Jon Skeet (2007).Groovy in Action. Manning. ISBN 1-932394-84-2.

    • Discusses Visitor, Builder and other Patterns.

  6. Brad Appleton (1999).Pizza Inversion - a Pattern for Efficient Resource Consumption.

    • One of the most frequently used patterns by many software engineers!

  7. Design Patterns in Dynamic Languages by Neil Ford.Design Patterns in Dynamic Languages.

4. Acknowledgements

4.1. Contributors

The Groovy team would like to thank the contributors of this documentation (in alphabetical order of last/surname):

4.2. License

This work is licensed under theApache License, Version 2.0.

Version 4.0.27
Last updated 2025-05-24 10:13:20 +1000

[8]ページ先頭

©2009-2025 Movatter.jp