A Brief,
Yet Thorough, Introduction into the Groovy Programming Language and
Development Kit
May 2, 2011 · Robert Söding
Preface
If there is one conservative language in the modern
programming world, then it's probably Java. Just take closures as
an example, which most other languages already support for years.
(Closures, finally, are to be integrated into Java 8, which is
expected to be released in 2012; however, their functional range to
date will be limited.)
When it comes to a programming language's "expressiveness", I,
personally, have been always in doubt about that: Programming
languages need to be readable, and thus, maintainable, at first
place (fortunately, Groovy is, to a large degree). Moreover, when
working in teams, rather "exotic" programming languages don't make
much sense - the ideal expressiveness of a language ought to be
near the common denominator within the team.
Thus, I well decided to work into common Java frameworks over
learning languages like Groovy or Scala.
Nonetheless, at some point, I did make myself familiar with
Groovy - and the reason was ... the
Grails framework, which I consider one of
"the" most advanced, at the same time convenient, and yet modularly
extensible, web frameworks, at all.
After having read one or two books on Grails, I'd been tempted
to do my first Grails project right away. That might have worked,
easily, but then again, I'd been so excited about Grails that I
wanted to "do it right" just from the beginning.
And so it came that I did my homework on Groovy, learning
syntax constructs and functional capabilities, but also estimating
Groovy's environmental impact, as well as its "good, bad, and ugly"
paradigms. - These are documented in the article at hand.
As Groovy leverages the Java language and its
class libraries, readers of this article are supposed to be already
proficient in Java. Discussing aspects of the Java language is out
of this article's scope.
Feedback and corrections are most welcome and may be directed
to . Thanks.
Introduction
Groovy has been created in 2003 by James Strachan, and was
first publically introduced in 2004. G2One, the company that had
backed Groovy and Grails formerly, has been acquainted by
SpringSource in 2008. Current project lead is Guillaume Laforge of
the former team.
Compiling to Java bytecode, leveraging the JVM, and being
interoperable with Java, Groovy adds the agileness and dynamic of
scripting languages like Ruby or Python to Java.
Features include closures, native syntax for collections,
regular expressions, variable interpolation into strings (GString),
an XPath-like object / property / child object navigation (GPath),
and operator overloading.
Groovy's meta programming capabilities allow for extending
objects at compile- or runtime, like adding or intercepting
methods, altering behaviour, etc.
As almost any Java constructs are valid in Groovy, the
learning curve is quite low. Moreover, most Groovy code is quite
intuitively understandable.
Besides Groovy's own, built-in, libraries, i.e., for SQL or
XML processing, or unit testing, there are several Groovy
modules and frameworks available, one of which
is the Spring-based
Grails framework.
Installing Groovy and Executing Groovy Code
Installing Groovy
Groovy depends on Java. More specifically, executing Groovy
files compiled to Java bytecode requires the Java Runtime
Environment (JRE).
The Groovy binaries as well as its Windows and Debian setup
packages can be downloaded from the
Codehaus
website. Alternatively, most Linx distributions ought to have
the
groovy package included in their repositories.
(However, as Groovy constantly evolves, the Groovy version that,
i.e., Ubuntu ships is rather outdated.)
Executing Groovy Code
The Groovy distribution comes with three commands to execute
Groovy code:
groovysh - opens the Groovy console to execute
Groovy code interactively
groovyConsole - opens a GUI program to execute
Groovy code interactively
groovy - starts the Groovy interpreter with the
given command line arguments
Additionally, there's the Groovy compiler,
groovyc.
Using the Groovy Shell
Using groovysh, code can be interactively
executed in the Groovy shell:
$ groovysh
Groovy Shell (1.7.5, JVM: 1.6.0_22)
Type 'help' or '\h' for help.
-------------------------------------------------------------------------------
groovy:000> println "Hello World"
Hello World
===> null
The help command may also take Groovy shell
commands as arguments, i.e., help history.
Interestingly, the inspect command opens the
Groovy Object Browser on the last evaluated
expression:
groovy:000> x = "Hello World"
===> Hello World
groovy:000> inspect
In the above screenshot, all (meta) methods are listed that
are available on an instance of
java.lang.String. In
addition to the Java methods, Groovy has added a whole bunch of new
methods (see chapter
The Default
Groovy Methods).
Using the Groovy Console
The groovyConsole command starts the Groovy
Console to interactively execute Groovy code:
Using the groovy Command
The groovy command allows for executing Groovy
files ...
$ groovy Hello.groovy
Hello World!
or a given command line parameter:
$ groovy -e "println 'Hello World'"
Hello World
Compiling Groovy Source Files
Groovy source files can be compiled to Java classes (Java
bytecode) by using the groovyc command, i.e., by
entering:
groovyc Hello.groovy
This will create a file named Hello.class - which
contains dependencies on the Groovy core runtime. In order to
execute it, the groovy-all*.jar file needs to be on the
classpath, i.e., (on Ubuntu):
java -cp /usr/share/java/groovy-all.jar:. Hello
The groovy-all.jar file in the above code snippet is
a symlink to
/usr/share/groovy/embeddable/groovy-all-1.6.4.jar on
Ubuntu. Alternatively, if you've downloaded and unpacked the Groovy
binary distribution, a corresponding JAR would be
$GROOVY_HOME/embeddable/groovy-all*.jar.
The decompiled Java bytecode, finally, doesn't look quite like
what you'd expect from a human-readable Java class. It also
contains references to a number of Groovy classes that "manage" the
resulting Java code, such as invoking method calls, dynamically.
(See chapter
Meta Programming for
more information.)
It's a good idea to take a look at the Java code
that Groovy generates by using a
Java
Decompiler.
Using Groovy from within IDEs
All major Java-based IDEs (Eclipse, NetBeans, IntelliJ IDEA,
JDeveloper, and others) support Groovy - either natively or by an
external plugin.
IDEA supports Groovy in its community edition, but Grails in
its commercial editions, only. As for Eclipse, there's a Groovy
plugin, but also the
SpringSource Tool Suite, that, moreover,
provides Grails support.
Unfortunately, Groovy code may contain
"virtual" methods added through
meta
programming. Additionally, Groovy allows for
dynamic typing, where types are resolved at
runtime, not at compile time. Thus, object introspection is
limited, impacting code completion and secure refactoring. Also,
IDE hints like "go to declaration" or "tooltips on mouseover" often
don't work.
The Groovy Language
"Groovy", as a language and framework, may be
categorized into the language itself, the GDK
(Groovy Development Kit), and a set of libraries that
shorten up common programming tasks. The following picture shows a
corresponding mind map:
This article mostly deals with Groovy's language and GDK
scopes; the Groovy built-in libraries are left out. (In future
Groovy versions, those libraries will be externalized from the
core, BTW.)
From Java to Groovy
Almost any Java code construct, and datatype, any type of Java
code, is valid in Groovy, as well. As such, Groovy may be regarded
a Java superset.
However, some syntax elements are optional in Groovy,
including parentheses, return statements, semicolons at the end
of statements, and package prefixes for common packages.
Java classes', methods' and properties' accessibility
modifiers (public, protected,
private, default) apply to Groovy as well,
however, the default modifier (which is then omitted)
doesn't mean package but public.
Bean properties, such as
person.setName(..) or person.getName()
can be accessed through their property name, i.e., by
person.name = "John", resp., assert person.name
== "John", just like in Java's Expression Language
(EL).
Groovy automatically imports the following packages:
groovy.lang.*
groovy.util.*
java.lang.*
java.util.*
java.net.*
java.io.*
java.math.BigInteger,
java.math.BigDecimal
Mostly due to its dynamic nature - missing compile time checks
-, Groovy code often makes heavily use of language-level
assertions, i.e.,
x = "hello"
assert x == "hello"
While Groovy does support Java generic types by
syntax, the Groovy compiler doesn't check them and, in fact, strips
them off before compilation. That means, a
List<Person>'s items can be assigned instances
of any type. (Java bytecode as well doesn't contain
information on generic types anymore, nevertheless, Java source
code that is invalid in this respect won't compile, at
least.)
Objects in Groovy
In Groovy, everything is an object: types, methods,
operators, closures. Unlike in Java, there are no primitive
types (instances may may be declared as int,
double, etc., in Groovy, however, are treated like
their Java wrappers, like Integer or
Double).
The Default Groovy Methods
The GDK (Groovy Development Kit) adds additional methods to
Objects and more specific types. Most of these methods
are defined by Groovy's
DefaultGroovyMethods class.
For a compilation of which methods are added to each type,
specifically, see the
GDK
ApiDoc.
Methods that are added to any Object
include:
// returns true if the closure returns true for any item:
Boolean any {closure}
// returns a list of all items that were returned from the closure:
List collect {closure}
// same as above, but adds each item to the given collection:
Collection collect(Collection collection) {closure}
// simply executes the closure for each item:
void each {closure}
// same as each{} except it passes two aruments: the item and the index:
void eachWithIndex {closure}
// returns true if the closure returns true for all items:
Boolean every {closure}
// returns the first item that matches the closure expression:
Object find {closure}
// returns all items that match the closure expression:
List findAll {closure}
// returns the index of the first item that matched the given expression:
Integer findIndexOf {closure}
Most of these methods are further described in
later chapters of this article.
Notably, the existence of these
additional methods does not mean that they would exist anywhere in
Java bytecode (by that exact signature). Nevertheless, they are
valid Groovy language constructs, and as such get transformed into
Java bytecode constructs during the Groovy compilation process (see
chapter
Meta Programming for an
understanding) by using the
corresponding methods in,
particularly,
DefaultGroovyMethods.
Optional Typing
Variables - such as local or instance variables, as well as
method or closure return values - may be statically typed, but
don't need to be:
Integer age = 22 // statically typed
int age = 22 // statically typed; evaluates to a java.lang.Integer
def age = 22 // dynamically typed; evaluates to a java.lang.Integer
age = 22 // same as above
def age = 22 as Integer // statically typed
If a variable has not been statically typed (by using the
def keyword or no qualifier), it is of type
Object under the hood. Thus, it can be assigned
instances of any other type:
def i = 123
assert i != "123"
i = "123"
assert i == "123"
It should be noted that such a type change may
easily cause runtime errors or unexpected data. For instance,
Groovy's each(..) method works differently on an
integer than on a string.
In contrast to the Java compiler, the Groovy compiler, per default
- and per common Groovy paradigm, does not perform compile-time
checks on matching types.
Duck Typing
Duck typing means, "if it walks like a duck and
quacks like a duck, it must be a duck".
Groovy's runtime provides any object with a large set of
additional methods - many of those also for objects of different
types. For example, there's the iterating each(..)
method on every object. Thus, the object type is easily
interchangeable because still the same methods can be used.
Obviously, the same (overloaded) methods may
produce different results when operating on objects of different
runtime types. For example, the each(..) method may
iterate over the items of a collection, the single characters of a
string, or, in case of an integer, just iterate one time. - It does
not really make sense to use that method with numbers.
In contrast to Java, which selects - overloaded -
methods based on an object's static type (and compiles that
information into bytecode), Groovy chooses matching methods based
on the runtime type of an object.
Duck typing may also lead to absurd behaviour
like, true.toBoolean() becoming false:
The toBoolean() method exists for
Strings, only; however, Groovy "auto-magically" ;-)
performs some miscarrying coercion.
There are also the
List respondsTo(String,
Object[]) and
List respondsTo(String) methods,
which take a method name (and a number of method parameters) and
return a
List<MetaMethod>. See the
JavaDoc and some
sample code for more information.
Simple Data Types
Primitives, like byte, int, and
long are all converted into their object wrappers
(i.e., Integer) in "everything is an
object"-Groovy.
As a result, numeric operations are relatively
costly in Groovy because of the then frequent need of conversion
between wrappers and their respective primitive types
(boxing / unboxing).
Numbers
The default number types in Groovy are Integer
and BigDecimal. If a variable is weakly typed, an
(alternatively, uppercase or lowercase) ordinal needs to be added
to its reference in order to implement a specific runtime
type:
def number
number = 0
assert number instanceof Integer
number = 0.0
assert number instanceof BigDecimal
// --------------------------------
number = 0L
assert number instanceof Long
number = 0D
assert number instanceof Double
number = 0G
assert number instanceof BigInteger
number = 0.0G
assert number instanceof BigDecimal
The Groovy runtime adds several additional methods to number
instances, including those that normally would be by
operators, including
plus(..),
multiply(..), etc.
In Java, the division of an int by another
int results in an int (such as 1 /
2 == 0 becomes true). Interestingly, that's different in
Groovy, where int/int divisions always
return a BigDecimal:
def bigDecimal0 = 1.div(2)
assert bigDecimal0 == 0.5
assert bigDecimal0 instanceof BigDecimal
def int0 = 1.intdiv(2)
assert int0 == 0
assert int0 instanceof Integer
Other methods of interest include those that faciliate
iteration:
def counter
counter = 0
5.times { counter++ }
assert counter == 5
counter = 0
1.upto(5) { counter++ }
assert counter == 5
counter = 0
5.downto(1) { counter++ }
assert counter == 5
def stepsList = []
0.step(10, 2) { stepsList << it }
assert stepsList == [0, 2, 4, 6, 8]
Object Operators
Operators in Groovy are shortcuts for corresponding methods.
For instance, a + b maps to the a.plus(b)
method.
Such "operator methods", added to Java classes by the GDK,
mainly exist for Collections, Maps, and
Numbers - however, they're added to classes of most
existing Java types.
Find below a list of operators and mapped methods:
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)
switch(a) { case(b) : } b.isCase(a)
~a a.bitwiseNegate()
-a a.negative()
+a a.positive()
The compilation below continues the above list, however, these
methods abstain from throwing a
java.lang.NullPointerException when evaluating against
null:
a == b a.equals(b) or a.compareTo(b) == 0
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
There are more operators in Groovy (i.e., the dot
operator that operates on properties and methods, the collections'
spread and subscript operators, RegEx operators, ...).
Operator Overloading
The following code snippet provides an example of (both)
overriding and overloading the == and +
operators:
class Counter {
private int count
Counter(int count) {this.count = count}
// override the == operator
@Override
boolean equals(Object other) {
if (!(other instanceof Counter))
return false
return this.count == other.count
}
int hashCode() {count.hashCode()}
// overload the + operator
// @Override
Counter plus(Counter other) {
if (other == null)
return null
return new Counter(this.count + other.count)
}
}
def counter = new Counter(2)
assert counter == new Counter(2)
assert counter + counter == new Counter(4)
As Groovy uses the reference types of arguments
to select an overloaded method (instead of using their static
types, like Java does), it would be also possible to overload the
boolean equals(Object) method (by boolean
equals(Counter)).
Compiling to statically typed Java bytecode
using
Groovy++'
@Typed
annotation would change back that behaviour.
BTW, the Groovy compiler does not
respect the @Override annotation.
Equality and Identity
In Groovy, the equivalent to Java's boolean
equals(Object) method is the == operator, which
in Java in turn tests for identity (whether both references share
the same address space in memory). To test for identity in Groovy,
the Boolean is(Object) is used.
Closures
Closures - like methods - are blocks of code that may take
parameters, do operations, call other methods (or closures), and,
finally, may return a result. Unlike methods, however, that may
return void (nothing), closures always return a value
- at least, null.
def printClosure = { println "Hello World!" }
// both calls are equivalent
assert printClosure.call() == null
assert printClosure() == null
def stringClosure = { "Hello World!" }
def nullClosure = { }
assert stringClosure() instanceof String
assert nullClosure() == null
Closures can declare parameters. If a closure does not declare
parameters, explicitly, there's always the implicit parameter
it. Parameters may have default values. Closures can
access variables, methods, etc., in the context of their enclosing
class.
def calcClosure = { x, y, z ->
x + y + z
}
assert calcClosure(1, 2, 3) == 6
def classNameClosure = { it.class.name }
assert classNameClosure(1.2) == "java.math.BigDecimal"
def paramClosure = { x, y, z = 0 ->
x + y + z }
assert paramClosure(1, 2) == 3
def contextVar = 2
def contextClosure = { it * contextVar }
assert contextClosure(3) == 6
Closures can be defined explicitly, by assigning it to a
variable, or implicitely. Real objects, they can be passed around -
for example, as method parameters.
def printClosure = { println "closure called" }
printClosure()
def callClosureMethod(closure) {
closure()
}
callClosureMethod { println "closure called" }
callClosureMethod printClosure
assert [1, 2, 3].find{ it == 1 }.toString() == "1"
Closures can also reference methods:
def quote(context) { 42 }
def quoteClosure = this."e
assert quoteClosure(13) != 14
Closures are often used when iterating over collections (and
thereby, testing for conditions or transforming items):
def map = [a:1, b:2, c:3]
map.each { println it }
map.each { key, value -> println key + "=" + value }
assert map.findAll { entry -> entry.value < 3 } == [a:1, b:2]
assert map.collect { it.value = it.value * 2 } == [2, 4, 6]
A closure's return statement doesn't propagate
beyond the closure's own scope. Therefor, in an iterating method
that applies a closure, the closure's return statement
will function like continue in a for
loop:
counter = 0
[1, 2, 3].each { counter++ }
assert counter == 3
counter = 0
[1, 2, 3].each {
if (it == 2) return "foo"
counter++
}
assert counter == 2
Closures have properties that let them access code in
(container or other) scopes: this refers to the
container class. owner refers to a container closure
(if any) or to this. delegate, by
default, refers to this, but can also be explicitly
set:
class Test {
def closure = {
def result = [:]
result << ["this": this.class.name]
result << ["owner": owner.class.name]
result << ["delegate": delegate.class.name]
def nestedClosure = {
result << ["nested owner": owner.class.name]
}
nestedClosure()
}
}
def testClosure = new Test().closure
testClosure.delegate = new Object()
assert testClosure.call() == [
"this": "Test",
"owner": "Test",
"delegate": "java.lang.Object",
"nested owner": 'Test$_closure1'
]
Currying
Currying means to take a function that takes multiple
parameters, provide some of those parameters with values, and
transform it to another function, which takes the same parameters
except the ones already provided:
def original = { x, y, z -> return x + y + z }
def curried1 = original.curry(2)
def curried2 = curried1.curry(4)
assert curried2(1) == 7
assert original.getParameterTypes().size() == 3
assert curried1.getParameterTypes().size() == 2
assert curried2.getParameterTypes().size() == 1
Currying is often used to obtain different behavior based on
configuration. In the example below, different filter and handler
"strategies" get applied.
def compositeIterator = { Closure filter, Closure handler, List valuesList ->
valuesList.each {
filter(it) ? handler(it) : null
}
}
def resultsList = []
def smallNumbersFilter = { it < 3 }
def oddNumbersFilter = { it % 2 == 0 }
def printHandler = { println it }
def addToListHandler = { list, var -> list << var }.curry(resultsList)
def config1 = compositeIterator.curry(smallNumbersFilter, printHandler)
def config2 = compositeIterator.curry(oddNumbersFilter, addToListHandler)
config1(1..5)
config2(1..5)
assert resultsList == [2, 4]
The book
Groovy in Action provides an
excellent logging system sample, where configuration options
include filtering of log messages (whether they contain the strings
"debug" or "info"), output formatting (i.e.,
{ line ->
"($new Date()): $line" }), and providing an appender (i.e.,
a console appender). - I just didn't dare to steal that
brilliant sample ...
Literals
Groovy Strings
def languageName = "Groovy"
def languageType = "dynamically typed"
println "$languageName is a $languageType language"
If the variable portion is more complex, like, it contains
spaces or dots, the variable placeholder needs to use curly
braces:
println "Current date is: ${new Date()}
The triple-quotation syntax supports multi-line
strings:
def person = "Joe"
println """Hello $person,
how are you?"""
Strings in single quotes (') do not support string
interpolation.
Groovy also supports
slashy syntax, starting and
ending with a slash. In such strings backslashes don't need to be
escaped. The slashy syntax comes handy with Windows filenames or
Regular Expressions, for
instance:
def fileName = /C:\Windows\Program Files/
assert "Hello"[0] == "H"
assert "Hello".getAt(1) == "e"
assert "Hello"[0..2] == "Hel"
assert "Hello".contains("el")
assert "Hello".indexOf("el") == 1
assert "x".padLeft(3, "_") == "__x"
assert "x".padRight(3, "_") == "x__"
assert "I am the walrus".tokenize() == ["I", "am", "the", "walrus"]
assert "hello".capitalize() == "Hello"
assert "Otto".reverse() == "ottO"
def greeting = "Hello"
greeting <<= " World!"
assert greeting instanceof StringBuffer
assert greeting.toString() == "Hello World!"
Regular Expressions
Under the hood, Groovy makes use of the Java
java.util.regex.Pattern and
java.util.regex.Matcher classes. It is highly
recommended to study the
corresponding JavaDocs.
Groovy provides support for Regular Expressions at the
language level, adding three convenience operators:
- The find operator:
=~,
which can be used to obtain a Matcher:
def matcher = "abc123" =~ /[\d+\D+]+/
assert matcher instanceof java.util.regex.Matcher
assert matcher.matches()
- The match operator:
==~,
which can be regarded a shortcut to the above sample code:
assert "abc123" ==~ /[\d+\D+]+/
- The pattern operator:
~/someRegEx/, which can be used to
obtain a compiled Pattern:
def pattern = ~/[\d+\D+]+/
assert pattern instanceof java.util.regex.Pattern
assert pattern.matcher("abc123")
assert pattern.matcher("a1b2c3")
assert pattern.matcher("123abc")
Note that compiling a Pattern (see
the pattern operator) is costly and slow in relation to
applying a Matcher to it. If planning to repeatedly
test for matches, it is advisable to compile the
Pattern once, and to reuse it.
Related GDK methods include the following:
result = ""
"a1b2c3".eachMatch(/\d/) { result += it }
assert result == "123"
result = ""
def matcher = "a1b2c3" =~ /\d/
matcher.each { result += it }
assert result == "123""
result = "a1b2c3".replaceAll(/\d/) { it - it + "_" }
assert result == "a_b_c_"
The standard pattern notation, such as
/\w+/ matches ASCII letters, only. To match Unicode
characters, like German Umlauts, French accented characters, or
Chinese characters, use an
extended notation, i.e.:
def pattern = ~/\p{L}+/
assert pattern.matcher("ä")
assert pattern.matcher("a")
assert ! pattern.matcher("1")
assert ! pattern.matcher("\n")
Lists, Maps, and Ranges
The semantics of List and Maps are
the same in Groovy and Java; however, Groovy adds expressive syntax
shortcuts to both. Groovy also introduces the concept of
Ranges, that doesn't exist in Java.
On the other hand, Groovy doesn't support Java's arrays by
syntax (int[] ints = {1, 2, 3} won't compile in
Groovy).
In contrast to Java, the Groovy "collective" types have
built-in support for the equals(..) method, by
applying that operation to all of their items.
Ranges
Ranges contain a number of elements, that can be iterated
through from a left to a right bound.
These elements that ranges contain don't need to be arranged
in constant intervals (i.e., "1 2 3 4", "1 2 4 8", "1 3 2 5" are
all ranges that can be iterated through), but there is always some
kind of strategy that the range will use to determine which element
to return next.
Obviously, one can iterate through a range of numbers, or
count up a date; however, ranges can contain elements of virtually
any object type, including train stations passed on a journey, or
species that came up during human evolution in history.
Using Ranges
Ranges can include or exclude their outer bounds, which is
expressed by the range operator:
left..right // inclusive range
left>..<right // exclusive range
left..<right // half-exclusive range
The range operator's operator precedence
is quite low (lower than a dot operator that connects objects from
their methods or properties) so that it is often necessary to put
the range operator in brackets.
Like almost anything in Groovy, ranges are objects that can be
referenced or assigned methods, etc. (In fact, there's the
groovy.lang.Range interface, which Ranges
implement).
Find below a number of sample uses of ranges:
// integer range
assert (1..10).contains(10)
// half-exclusive range
assert (1..<10).contains(10) == false
// string range
assert ("a".."c").contains("b")
// date range
def today = new Date()
def tomorrow = today + 1
assert (today..tomorrow)[0] == today
assert (today..tomorrow)[1] == tomorrow
// range reference
def r = 1..10
assert r instanceof Range
// loop, and reverse range
def result = ""
for (x in 5..1) {
result += x
}
assert result == "54321"
// each with closure
result = ""
(1..5).each {
result += it
}
assert result == "12345"
Implementing a Custom Range
To implement a custom
Range,
- create a class that represents an element of the range
to be realized,
- implement the
java.lang.Comparable interface with
its int compareTo(Object) method,
- implement a custom
Object next() method
- and a custom
Object previous() method.
Find below a simple example:
class IntegerRange implements Comparable {
int value
IntegerRange(int value) {
this.value = value
}
int compareTo(Object other) {
this.value.compareTo(other.value)
}
IntegerRange next() {
new IntegerRange(value + 1)
}
IntegerRange previous() {
new IntegerRange(value - 1)
}
}
def min = new IntegerRange(1)
def max = new IntegerRange(5)
def result = ""
(min..max).each { result += it.value }
assert result == "12345"
Under the hood, Groovy will pass two instances
(left and right bound) of this custom class to a
groovy.lang.ObjectRange instance, which will invoke
the
next() and
previous()methods by
meta programming, finally.
Ranges can consist of values of any kind and
type, that can be traversed sequentially. Examples include a list
of songs in an album, a list of train stations on a journey, or -
as in the following sample - a number of phases in a fixed workflow
- called Workloads in this case.
class Workload implements Comparable {
private static final PHASES = ["plan", "implement", "test", "deploy"]
private int currentPhase
Workload(String phase) {
this.currentPhase = PHASES.indexOf(phase)
}
@Override
int compareTo(Object other) {
this.currentPhase <=> other.currentPhase
}
Workload next() {
new Workload(PHASES[currentPhase + 1])
}
Workload previous() {
new Workload(PHASES[currentPhase - 1])
}
@Override
String toString() {
PHASES[currentPhase]
}
}
def firstPhase = new Workload("plan")
def lastPhase = new Workload("deploy")
def phases = []
(firstPhase..lastPhase).each { phases << it }
assert phases.join(", ") == "plan, implement, test, deploy"
Lists
Lists in Groovy are of type ArrayList by default,
which is (like any other List type)
ordered.
Groovy does not support generic types (even not at
compile time), thus list items can be assigned references of any
type.
Specifying Lists
As for creating and specifying lists, there are a number of
ways:
// create a list of integers
list = [1, 2, 3]
assert list.size() == 3
// by default, lists are of type java.util.ArrayList
assert list instanceof ArrayList
// explicitly construct an instance of another List type
def linkedList = new LinkedList([1, 2, 3])
assert linkedList instanceof LinkedList
assert linkedList.poll() == 1
// create an empty list
list = []
assert list.size() == 0
// use regular java.util.List methods
linkedList.addAll(list)
assert linkedList.size() == 2
// create a nested list
list = [1, 2, [1, 2, 3]]
assert list[2].size() == 3
assert list[2] == [1, 2, 3]
// lists can take any object type
list = [1, 2, 3.14, "baz", new Object()]
assert list.size() == 5
// generic type information is stripped before compilation
List<Integer> intList = new ArrayList<Integer>([1, 2, 3.14, "baz", new Object()])
assert list.size() == 5
That's all quite intuitive. Notably, however, Groovy does not
support Generics, so any object type is valid within a list -
including other, then nested, lists.
Accessing and Updating List Elements
List elements are usually accessed, or updated, by using the
subscript operator, which may also take a range.
Additionally, there are GDK methods that allow for iterating over
lists and filtering items.
list = [1, 2, 3]
// using the subscript operator
assert list[0] == 1
// using the GDK method
assert list.getAt(0) == 1
// using a range in a subscript operator
assert list[0..<2] == [1, 2]
// set an element
list[0] = 9; assert list[0] == 9
list.putAt(0, 10); assert list[0] == 10
list[0..1] = [5, 4]; assert list == [5, 4, 3]
// (revert to original list)
list = [1, 2, 3]
// find a single item by condition
assert list.find { it < 3 } == 1
// find all items by condition
assert list.findAll { it < 3 } == [1, 2]
// iterate through a list
result = ""
list.each { result += it }
assert result == "123"
Adding and Removing List Elements
Adding and removing items can be performed by using the
appropriate GDK methods (see the
DefaultGroovyMethods ApiDocs)
or their corresponding operators:
// add an item to a collection
assert [1, 2, 3] == [1, 2] + 3
// by using the plus(Collection, Object) method
assert [1, 2, 3] == [1, 2].plus(3)
// add another collection
assert [1, 2, 3, 4] == [1, 2] + [3, 4]
// by using the plus(Collection, Collection) method
assert [1, 2, 3, 4] == [1, 2].plus([3, 4])
// add an item to a collection
assert [1, 2, 3] == [1, 2] << 3
// by using the leftShift(Collection, Object) method.
assert [1, 2, 3] == [1, 2].leftShift(3)
// add an item to a collection (same as above),
// but the item is a collection itself,
// so that we'll get a nested list
assert [1, 2, [1, 2, 3]] == [1, 2] << [1, 2, 3]
assert [1, 2, [1, 2, 3]] == [1, 2].leftShift([1, 2, 3])
// remove an item
assert [1, 3] == [1, 2, 3] - 2
// by using the minus(List, Object) method
assert [1, 3] == [1, 2, 3].minus(2)
// remove an entire collection
assert [1] == [1, 2, 3] - [2, 3]
// by using the minus(List, Collection) method
assert [1] == [1, 2, 3].minus([2, 3])
// multiplying the element instances within the collection
assert [1, 2, 3, 1, 2, 3] == [1, 2, 3] * 2
// by using the multiply(Collection, Number) method
assert [1, 2, 3, 1, 2, 3] == [1, 2, 3].multiply(2)
Using Lists in Control Structures
Lists can be used in control structures as follows:
// an empty list evaluates to boolean false
if ([]) { assert false }
assert [1, 2, 3]
// lists can be traversed in for-loops
def result = ""
for (x in [1, 2, 3]) { result += x }
assert result == "123"
// lists can be traversed by using the each() method
result = ""
[1, 2, 3].each { result += it }
assert result == "123"
// lists can be used in switch statements
switch (2) {
case [4, 5, 6] : assert false; break
case [1, 2, 3] : assert true; break
default : assert false
}
assert [1, 2, 3].isCase(2)
// there are also methods to filter results without looping through a list:
assert [1, 2, 3].intersect([2, 3, 4]) == [2, 3]
assert [1, 2, 3].grep([2, 3, 4]) == [2, 3]
// as for transforming results without looping:
assert [1, 2, 3].collect { it * 2 } == [2, 4, 6]
Manipulating List Elements
There are a number of methods to manipulate list
elements:
// integrate nested lists
assert [1, [2, 3]].flatten() == [1, 2, 3]
// reverse list contents
assert [1, 2, 3].reverse() == [3, 2, 1]
// sort a list
assert [3, 1, 2].sort() == [1, 2, 3]
assert ["b", "a", "c"].sort() == ["a", "b", "c"]
assert [true, false, true, false].sort() == [false, false, true, true]
assert ["hi", "hey", "hello"].sort { it.length() } == ["hi", "hey", "hello"]
// remove an item from a list
// by index
def list = ["a", "b", "c"]
assert list.remove(0) == "a"
assert list == ["b", "c"]
// by value
assert list.remove("c") == true
assert list == ["b"]
// remove multiple items from a list
list = [1, 2, 3, 4, 5]
assert list.removeAll([8, 9]) == false
assert list.removeAll([2, 3, 4, 8, 9]) == true
assert list == [1, 5]
// transform a list into another
assert [1, 2, 3].collect { it * 2 } == [2, 4, 6]
// remove null values
assert [1, 2, null].grep { it } == [1, 2]
// remove duplicate values
assert [1, 1, 2].unique() == [1, 2]
More Methods on Lists
The following methods apply for querying for, iterating over,
and accumulating list elements:
list = [1, 2, 3]
// query for general information
assert list.size() == 3
assert list.min() == 1
assert list.max() == 3
// find a single item by condition
assert list.find { it < 3 } == 1
// find all items by condition
assert list.findAll { it < 3 } == [1, 2]
// determine if the given closure matches for all list items
assert list.every { it < 3 } == false
assert list.every { it < 4 } == true
// concatenate list items
assert list.join(", ") == "1, 2, 3"
// iterate through a list
result = ""
list.each { result += it }
assert result == "123"
result = ""
list.reverseEach { result += it }
assert result == "321"
// instead of defining the "result" variable outside the closure's own scope,
// we can also pass it
assert list.inject("") { result, element -> result += element } == "123"
Maps
The default implementation in Groovy is a
java.util.LinkedHashMap, which, in contrast to a
HashMap, is ordered.
Specifying Maps
Maps are built of key/value pairs of the shape
[key1:value1, key2:value2, key3, value3], and are
accessed by using any key with the subscript operator (i.e.,
map["key1"].
// create an empty map
def emptyMap = [:]
// create a filled map
def map = ["a":1, "b":2, "c":3]
assert map.size() == 3
// maps, per default, are of type java.util.LinkedHashMap (which is ordered)
assert map instanceof LinkedHashMap
// create a map of another type (TreeMap is sorted)
def treeMap = new TreeMap(map)
assert treeMap == map
// set a value
map["a"] = 2
assert map["a"] == 2
// keys can be specified without quotes
// if they do not contain special characters or are Groovy keywords
assert [a:1] == ["a":1]
// keys in parentheses map to local variables
def x = "a"
assert [(x):1] == [a:1]
Accessing, Updating, Adding, and Removing Map Entries
A map entry's value is accessed, or assigned, by using the
entry's key. Mostly, the key is specified within the subscript
operator, however, there are a number of other methods, including
using the dot-key syntax.
def map = [a:1, b:2, c:3]
// access a value by key
assert map["a"] == 1
// assign a value
map["a"] = 2
assert map["a"] == 2
// (revert)
map["a"] = 1
// different methods to access a value by key
assert map["a"] == 1 // subscript operator
assert map.a == 1 // dot-key syntax
assert map.get("a") == 1 // JDK method
assert map.get("a", 0) == 1 // default value
// dot-key syntax with keys that contain a dot
def dotMap = ["a.b":1]
assert dotMap."a.b" == 1
// query for a non-existent key
assert map.get("foo") == null
assert map.get("foo", 0) == 0 // default value
// NOTE that the latter GDK method has a
// side effect of potentially inserting a map entry,
// which ought to be considered a design flaw
assert map.containsKey("foo")
// (revert)
map.remove("foo")
assert map.containsKey("foo") == false
// adding a map entry
map.put("d", 4) // JDK method
map.putAll([e:5]) // JDK method
map.leftShift([f:6]) // GDK method
map << [g:7] // GDK leftShift operator
map["h"] = 8 // GDK subscript operator
assert map == [a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8]
// updating an entry
map["a"] = 8 // subscript operator
assert map["a"] == 8
map.a = 9 // dot-key syntax
assert map.a == 9
map << [a:10] // leftShift operator
assert map["a"] == 10
map.put("a", 1) // JDK method
assert map["a"] == 1
// removing an entry
assert map.remove("a") == 1
assert map == [b:2, c:3, d:4, e:5, f:6, g:7, h:8]
Using Maps in Control Structures
There are several ways to iterate over a map's entries:
def map = [a:1, b:2, c:3]
def result = ""
map.each { entry ->
result += entry.key
result += entry.value
}
assert result.contains("a1")
result = ""
map.each { key, value ->
result += key
result += value
}
assert result.contains("a1")
result = ""
map.keySet().each { result += it }
assert result == "abc"
result = ""
for (key in map.keySet()) {
result += key
}
result = ""
map.values().each { result += it }
assert result == "123"
result = ""
for (value in map.values()) {
result += value
}
assert result == "123"
// injecting the object to be returned into the closure
// instead of manipulating an object in the outer scope (see the "result" variable above)
assert map.inject([:]) { newMap, entry ->
entry.value *= 2
newMap << entry
} == [a:2, b:4, c:6]
More Methods on Maps
Additional methods that operate on maps include the
following:
def map = [a:1, b:2, c:3]
// JDK LinkedHashMap, although it is ordered,
// does not override HashMap#equals(Object),
// which does not account for the entries' ordering
def otherMap = [b:2, a:1, c:3]
assert map == otherMap
assert map instanceof LinkedHashMap
// JDK methods
assert map.isEmpty() == false
assert map.size() == 3
assert map.containsKey("a")
assert map.containsValue(1)
assert map.keySet() == new HashSet(["a", "b", "c"])
// (the freakin' HashMap.values() method returns an instance of type
// HashMap.Values instead of returning a standard List implementation)
assert map.values() != [1, 2, 3]
assert new ArrayList(map.values()) == [1, 2, 3]
// (entrySet() returns an HashMap.EntrySet instance)
assert map.entrySet().class.name == 'java.util.HashMap$EntrySet'
// GDK methods that test if the closures' conditions can be applied
assert map.any { entry -> entry.key == "a" }
assert map.every { entry -> entry.value > 0 }
// obtain a subset of a map
def subMap = map.subMap(["a", "b"])
subMap["a"] != 9
assert map["a"] == 1
// filter all matching entries
assert map.findAll { entry -> entry.value <= 2 } == [a:1, b:2]
// filter all matching entries, and return the first
def firstEntry = map.find { entry -> entry.value <= 2 }
assert firstEntry.key == "a"
// apply some operation on every entry
def entriesList = map.collect { entry -> entry.value *= 2 }
assert entriesList instanceof List
assert entriesList[0].value == 2
Using the * Spread Operator to Spread Collections
Apart
Ranges, lists, and maps can be split into their items by using
the * spread operator:
def list = [1, 2, 3]
assert [*list, 4, 5] == [1, 2, 3, 4, 5]
def map = [a:1, b:2]
assert [*:map, c:3] == [a:1, b:2, c:3]
def range = 3..5
assert [1, 2, *range] == [1, 2, 3, 4, 5]
// -------------------------------------
def multiply(a, b, c) {
return a * b * c
}
assert multiply(*list) == 1 * 2 * 3
Control Structures
The Groovy Truth
In Java, conditional expressions must evaluate to
boolean, whereas in Groovy, they may apply to any
object:
// boolean values
assert true
assert !false
// matchers must match
assert "a" =~ /\w/
assert !("" =~ /\w/)
// collections must not be empty
assert [1]
assert ![]
// maps must not be empty
assert [a:1]
assert ![:]
// strings must not be empty
assert "a"
assert !""
// numbers must not be zero
assert 1
assert -1
assert 1.1
assert 1.1f
assert !0
assert !0.0
// any object must not be null
assert new Object()
assert !null
Miscellaneous Control Structures
The switch Statement
In Java, switch statement classifiers
may be of the primitive types byte,
short, char, and int (and
their respective object wrappers), as well as enums.
In contrast, in Groovy, switch classifiers can be of
any object type, including Closures, regular
expressions' Patterns, and Classes.
switch (1) {
case 0 : assert false; break;
case 1.1 : assert false; break;
case 2..5 : assert false; break;
case [2, 3, 4] : assert false; break;
case {it == 2} : assert false; break; // Closure
case BigInteger : assert false; break; // Class
case ~/\d/ : assert true; break; // Pattern
default : assert false; break;
}
Unlike in Java, a
candidate may match multiple
classifiers. Whether or not a candidate is eligible to match a
classifier, is determined by the
isCase(..) method
that is defined in the
DefaultGroovyMethods class,
and is added to each Groovy object instance by the GDK. (The
isCase(..) method may, of course, be overwritten in
custom implementations.)
| Class |
a.isCase(b) is implemented as ... |
Class |
a.isInstance(b) |
Collection (including Range) |
a.contains(b) |
Number |
NumberMath.compareTo(a, b) == 0 |
Object |
a.equals(b) |
Pattern |
a.matcher(b.toString()).matches() |
String |
a.equals(b) |
Assertions
In contrast to Java, where assertions need to be enabled by
setting a command line parameter when starting the JVM, assertions
in Groovy are enabled by default.
There is a potential controversy about whether to
use assertions in production code, or not. - The authors of the
book
Groovy in Action advocate pro doing so: Assertions
did not impact performance too much, and if they did, they could be
placed into a static initializer block, for instance. In Java,
assertions were seldomly used because they'd need to be enabled -
and one could never be sure if they were.
The Groovy Documentation, as well,
advocates "Introduce Assertions" as one of the
most basic refactoring patterns in Groovy.
In contrast to most assertions in the article at hand,
assertions may be guarded with a meaningful message, i.e.:
file = new File("foobar")
assert file.exists(), "File '$file.name' does not exist"
The for loop
The different for loop notations equal Java's
except from Groovy's for (variable in
iterable) (which is for (variable :
iterable) in Java.
Groovy's overloaded
DefaultGroovyMethods.iterator(..) methods make many
types of objects iterable that are not iterable in Java. (This will
also provide an each(..) method to those types.)
If an object can't be made iterable - numbers, for instance -,
the body of a for loop will be processed once. If the
iterable is null, the for loop's
body won't be processed, at all:
def processed
for (x in new Object()) {
assert x.toString().startsWith("java.lang.Object")
processed = true
}
assert processed
for (x in null) {
assert false
}
Return Values
The return statement is optional both in methods
and closures. If omitted, the value of the last evaluated
expression will be returned. Whereas closures always return a value
(null at least), methods that explicitly specify a
void return type do not return a value:
void voidMethod() { }
def stringMethod() { "foo" }
// void nullClosure = { } // generates a GroovyCastException
def nullClosure = { }
def stringClosure = { "foo" }
assert voidMethod() == null
assert stringMethod() == "foo"
assert nullClosure() == null
assert stringClosure() == "foo"
In closures used with iteration methods such as
each(..), the return statement does not
work as one might expect (stop the loop, and, optionally, return a
given value). Instead, it functions like Java's
continue statement in a for loop:
def counter = 0
(1..5).each {
if (it == 3) return
counter++
}
assert counter == 4
Exception Handling
Groovy code does not throw checked exceptions and
does not require to declare or catch those when using Java methods
that explicitly throw checked exceptions.
The Java compiler, javac, however, complains when
checked exceptions are caught where they have not been declared. In
such cases it is necessary to add a corresponding
throws statement to the code being called.
Dynamic Object Orientation
Fields
As with methods and closures, the default visibility
is public. - Groovy doesn't support Java's
package default visibility.
Fields can be referenced by using the dot operator
(object.fieldName syntax) or the subscript
operator:
class Foo {
def bar
}
def foo = new Foo()
def fieldName = "bar"
foo[fieldName] = "baz"
assert foo.bar == "baz"
Actually,
foo.bar does not access
the field
bar, directly, but one of the accessors
getBar() or
setBar(..), that have been
automatically
generated by the Groovy compiler.
Methods
As with fields and closures, the default visibility
of methods is public. - Groovy doesn't support Java's
package default visibility.
Declarations of methods' return types and of parameter types
are optional. If omitted, they are untyped and their
return value is of type Object or
void:
// methods with explicitely typed return values
int intMethod() {1}
void voidMethod() { }
// methods with untyped return values
def untypedMethod1() {1}
def untypedMethod2() { }
// static methods
static void main(args) { }
def static main(args) { }
Unlike Java, Groovy supports optional parameters:
def foo(x, y = 0) { }
Constructors
There are a number of ways to call a constructor with
positional parameters:
class Person {
String firstName, lastName
Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
}
def person1 = new Person("John", "Doe")
def person2 = ["John", "Doe"] as Person
Person person3 = ["John", "Doe"]
Note that person2 and person3 are
constructed by passing a List that contains the
ordered parameters to be passed to the constructor.
When not specifying a constructor, Groovy - just like
Java - will create a default constructor. While Java's default
(implicit) constructor does not specify constructor arguments,
Groovy adds the capability to pass named parameters to the
default constructor:
class Person {
String firstName, lastName
}
new Person()
new Person(firstName: "John")
new Person(firstName: "John", lastName: "Doe")
Under the hood, the Groovy compiler does not
actually create any special constructors for named constructor
arguments to work. At the calling site, the nullary
constructor is invoked, and next, are the corresponding setters. -
So, that's no magic, but just a convention for code
generation.
Scripts, Classes, the Classpath, and the Groovy Compiler
Groovy files may contain one ore more class
definitions and/or code outside of classes. When compiled to Java
bytecode using the groovyc command, for each class a
single .class file is generated, which name is based on
the class' name. Such Groovy classes extend
GroovyObject.
Of code outside of class definitions in Groovy files, a
distinct .class file is generated, which name is based on
the Groovy file's name, extending the abstract
Script class, and containing a static void
main(..) method.
Like in Java, packages are represented by folders
in the file system; so the Groovy compiler will place a class
foo.bar.Person in the corresponding subfolder
foo/bar. The same rule applies when searching for classes
being referenced in code to compile (or execute): They need to be
located in a folder hierarchy that matches their
package declaration.
Like Java, as well, Groovy uses the
classpath
concept.
groovy and
groovyc try to locate
referenced classes at the following locations:
$JAVA_HOME/lib &
$JAVA_HOME/lib/ext
- The
$CLASSPATH variable as either set as an
operating system setting or manually, in a command shell
- Java's
-cp or --classpath command
line parameter
- Groovy's
$GROOVY_HOME/lib folder
- Groovy's
-cp command line parameter
- The current directory of execution:
.
- By default configuration,
${user.home}/.groovy/lib/*
The latter configuration option can be set/unset
in the $GROOVY_HOME/conf/groovy-starter.conf file. On
Ubuntu, it's located at
/etc/groovy/groovy-starter.conf.
Multimethods
When it comes to overloaded methods, Java dispatches the call
to a method whose parameters matches the static,
compile-time, reference types of the variables to pass, i.e.,
public class MethodTest {
private static String someMethod(String param) {
return "string";
}
private static String someMethod(Object param) {
return "object";
}
public static void main(String[] args) {
Object objectVar = new Object();
Object stringVar = new String();
System.out.println(someMethod(objectVar)); // "object"
System.out.println(someMethod(stringVar)); // "object"
}
}
Groovy, in contrast, dispatches method calls based on the
dynamic, runtime, types:
Object objectVar = new Object()
Object stringVar = new String()
def someMethod(String param) { return "string" }
def someMethod(Object param) { return "object" }
assert someMethod(objectVar) == "object"
assert someMethod(stringVar) == "string"
In consequence, the boolean
Object.equals(Object) method can be overloaded in
Groovy - not just overwritten as in Java.
GroovyBeans
JavaBeans are objects that
- have public accessors (getters and setters), while their
instance fields are private (information hiding
principle),
- have a nullary constructor (a constructor with no
arguments),
- are (optionally) serializable in order to be persisted
or passed around,
- can be inspected by using Java introspection, resp.,
reflection.
Additionally, JavaBeans may have attached events that may inform
listeners on state changes, etc.
A typical JavaBean might look like the following code
sample:
public class Person implements java.io.Serializable {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
(If no constructor exists, a default constructor will be
automatically generated at compile time.) The same bean in Groovy
does not have getters and setters:
class Person implements Serializable {
int id
String name
}
Automatic Generation of Getters and Setters
The Groovy compiler will automatically add corresponding
accessors (getters and setters), and make the fields' accessibility
private.
There are a number of rules on how to influence accessors
generation:
- If a field's accessibility is
private or -
explicitly marked - public, no accessors will be
generated.
- If there's the
final modifier on a field, no
setter will be generated (but still a getter).
- If there are corresponding accessors in Groovy code already,
they won't be overwritten at compile time.
Additionally, if a field is explicitly marked
public,
for instance, the Groovy compiler will not change that.
The accessors can be accessed as follows:
def person = new Person(id: 1, name: "John")
assert person.id == 1
assert person.getId() == 1
Event Handling
As per
JavaBeans specification (and common
practice, as well),
event listeners can register with a
JavaBean to be notified on miscellanious events. Such event
listeners implement interfaces that extend the
EventListener interface and typically define a single
method that the bean will call when a matching event occurs.
In Java, such event listeners are implemented as anonymous
inner classes. Doing so with a JButton would be a
prominent example:
final JButton button = new JButton("Click me!");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button '" + button.getText()
+ "' has been clicked.");
}
});
Note that anonymous inner classes - unlike Groovy
closures - can only access final instance variables of
their container class.
With the help of an additional method, that Groovy magically
adds, and a closure, the above code snippet becomes much more
concise in Groovy:
def button = new JButton("Click me!")
button.actionPerformed = {
println("Button '" + button.text
+ "' has been clicked.");
}
Querying Objects with GPaths
GPath expressions evaluate an object graph of field
accesses, property accesses, or method calls. As far as collections
(lists, maps, ranges) are involved, the corresponding filters and
operations can be applied, as well as the [] subscript
operator.
assert String.class.methods.size() == 72
assert String.class.methods.name.grep(~/getC.*/) == ["getChars", "getClass"]
The dot-spread *. operator is used to
apply a method to all items in a list, rather than to the list
itself:
class Rectangle {
int width
int height
String color
def area() { width * height }
}
def rectangles = [
new Rectangle(width: 10, height: 15, color: "blue"),
new Rectangle(width: 20, height: 30, color: "black"),
new Rectangle(width: 40, height: 60, color: "foo")
]
assert rectangles.width == [10, 20, 40]
assert rectangles.grep{ it.width == 40 }[0].color != "blue"
assert rectangles*.area() == [150, 600, 2400]
In traversing tree structures, like in XML processing, GPath
expressions can be used, too. XML attributes are then accessed by
using the @ at operator:
def xml = """
<items>
<item id="1" name="foo" />
<item id="2" name="bar" />
<item id="3" name="baz" />
</items>
"""
def items = new XmlSlurper().parseText(xml)
assert items.item.size() == 3
assert items.item.find{ it.@id == 2 }.@name == "bar"
This article does not actually cover Groovy
libraries. GPath expressions' fields of application, however, are
not limited to normal POJOs.
Safe Dereferencing with the ?. Operator
When evaluating a path expression, objects within
that path may be null; so we'd either need to check
for null values in code or catch a
NullPointerException. In Groovy, however, the safe
dereferencing operator ?. provides an elegant
solution for that problem:
class Foo {
def bar = 1
}
def foo
assert foo?.bar == null
Meta Programming
While Groovy applications are pure Java, Groovy adds
additional capabilities to the Java language and existing classes.
Changing the behavior of programming languages by the means of
programming is called meta programming.
At
compile time, the Groovy compiler goes through a
multi-phased process of creating an
Abstract Syntax Tree (AST) from Groovy
application code, that then gets transformed, finally getting
transformed to Java bytecode. Within the different phases of that
transformation, there are several entry points for programs to
alter that process either globally or locally (i.e., per class or
package). Application behaviour arisen from
compile-time meta
programming is statically compiled into Java bytecode.
A non-accidental side effect, compile-time type checks become
possible.
Groovy also allows for altering application behaviour, i.e.,
adding or overriding methods or properties, or by reacting on calls
to missing methods, at runtime. Such application behaviour
is heavily based on the application's runtime state. For this to
work, Groovy employs a complex system of stateful structures,
decision rules, and invocation flows, that is involved with each
method, constructor, or accessor, call.
While Groovy's runtime meta programming is extremly
flexible, it doesn't allow for most compiler checks. Additionally,
such code performs worse because of its complexity and dynamic
invocation.
Compile-Time Meta Programming
Using Delegation
The delegator pattern is an alternative to using
inheritance. The field-level @Delegate
annotation makes methods and properties of the composite
elements available as if they would natively exist at the container
element.
class Swimmer {
def swim() { "swimming" }
}
class Runner {
def run() { "running" }
}
class Biker {
def bike() { "biking" }
}
class Triathlete {
@Delegate Swimmer swimmer
@Delegate Runner runner
@Delegate Biker biker
}
def triathlete = new Triathlete(
swimmer: new Swimmer(),
runner: new Runner(),
biker: new Biker()
)
triathlete.swim()
triathlete.run()
triathlete.bike()
According to the Groovy documentation, "Replacing
Inheritance by Delegation" is one of the most basic design pattern
in
Groovy refactoring.
Introducing New Methods into Existing Classes Using
Categories
New methods can be added to existing classes at compile
time.
The methods to be added are defined as static
methods in so-called categories. These methods have at
least one parameter, where to the first parameter the reference to
the delegating object is passed:
class Day {}
class Soul {}
class PersistenceCategory {
static void save(Day day) {
println "day saved"
}
static void save(Soul soul, message) {
println "life saved, " + message
}
}
These methods are only accessible from within the
use(..) method:
use (PersistenceCategory) {
new Day().save()
new Soul().save("thanks")
}
Optionally, the use(..) method may also provided
with a comma-separated list of categories.
Alternatively to the above notation, the
@Category annotation can be
used.
Intercepting Method Calls of a Target Object
Methods of arbitrary objects (not necessarily
GroovyObjects) can be intercepted by implementing the
Interceptor interface, decorating the target class with a
ProxyMetaClass proxy instance, and assigning the
interceptor to the instance.
The Interceptor interface is defined as
follows:
public interface Interceptor {
Object beforeInvoke(Object object, String methodName, Object[] arguments);
Object afterInvoke(Object object, String methodName, Object[] arguments, Object result);
boolean doInvoke();
}
The Groovy runtime provides, for example, a
TracingInterceptor implementation. It can be used as
in the following code snippet:
import org.codehaus.groovy.runtime.StringBufferWriter
class SomeClass {
def someMethod() {
return "something"
}
}
def log = new StringBuffer("\n")
def tracer = new TracingInterceptor()
tracer.writer = new StringBufferWriter(log)
def proxy = ProxyMetaClass.getInstance(SomeClass.class)
proxy.interceptor = tracer
proxy.use {
assert new SomeClass().someMethod() == "something"
}
assert log.toString() == """
before SomeClass.ctor()
after SomeClass.ctor()
before SomeClass.someMethod()
after SomeClass.someMethod()
"""
Runtime Meta Programming
Introduction
Almost all objects in Groovy implement the
GroovyObject interface:
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);
}
Java objects can implement that interface, too,
and optionally extend its default implementation,
GroovyObjectSupport.
Groovy
Scripts are the only objects that do not implement
the
GroovyObject interface. However, these objects
extend
GroovyObjectSupport, too.
The GroovyObject's associated
MetaClass contains methods for invoking methods,
constructors, setting or returning properties (and lots more
...):
public interface MetaClass extends MetaObjectProtocol {
Object invokeConstructor(Object[] arguments);
Object invokeMethod(Object object, String methodName, Object[] arguments);
Object invokeStaticMethod(Object object, String methodName, Object[] arguments);
Object getProperty(Object object, String property);
void setProperty(Object object, String property, Object newValue);
// additional methods omitted ...
}
While each GroovyObject has its
MetaClass association, a
MetaClassRegistry implementation associates
MetaClasses with object Classes.
When a method call is processed, it can be - actually -
handled by the
invokeMethod(..) method of different
objects:
- the current object's own
invokeMethod(..)
implementation,
- the current object's
MetaClass'
invokeMethod(..) implementation,
- or by a
MetaClass' invokeMethod(..)
implementation, where the Metaclass instance had been
selected by the MetaClassRegistry.
The decision flow for the above options should, to some
extend, match the one described below:
- If the object in call is not an instance of
GroovyObject, method (3) will be chosen.
- Else, if the current "method" in call is a closure, it will be
invoked, directly (1).
- Else, if the method in call is not statically available at the
object in call, the object's
invokeMethod(..) is
dispatched (1).
- Else, if the object in call is an instance of
GroovyInterceptable, the object's
invokeMethod(..) is dispatched (1).
- Else, the object's
MetaClass'
invokeMethod(..) is dispatched (2).
Dynamically Expandable Beans (Expando
Objects)
Beans that are instances of the Expando class can
be created at runtime, on the fly, and can be expanded by
dynamically adding properties or methods to them:
def person = new Expando()
// dynamically adding a new property
assert person.name == null
person.name = "Fred"
assert person.name == "Fred"
// dynamically adding a new method
assert person.sayHello == null
person.sayHello = {
greeting -> greeting + delegate.name
}
assert person.sayHello("Hi, my name is ") \
== "Hi, my name is " + person.name
Using ExpandoMetaClass to Dynamically Add Methods,
Etc., to Existing Objects
The following code snippet demonstrates how to add a "meta"
method to an existing object:
class Person { }
person = new Person()
person.metaClass.sayHello = { "hello" }
assert person.sayHello() == "hello"
Using GroovyObject's invokeMethod(..)
when Methods are Missing
A GroovyObject can handle missing method calls
itself:
class Dog {
def invokeMethod(String name, args) {
switch (name) {
case "bark": bark(); break;
case "yowl": yowl(args[0]); break;
default: null
}
}
private def bark() {
println "wow! wow!"
}
private def yowl(intensity) {
print "wh"
intensity.times { print "oooh" }
println "!"
}
}
def dog = new Dog();
dog.bark()
// prints "wow! wow!"
dog.yowl(5)
// prints "whooohooohooohooohoooh!"
That's also what Groovy builders do (the sample stems
from the Groovy User Guide):
class XmlBuilder {
def out
XmlBuilder(out) { this.out = out }
def invokeMethod(String name, args) {
out << "<$name>"
if(args[0] instanceof Closure) {
args[0].delegate = this
args[0].call()
}
else {
out << args[0].toString()
}
out << "</$name>"
}
}
def xml = new XmlBuilder(new StringBuffer())
xml.html {
head {
title "Hello World"
}
body {
p "Welcome!"
}
}
Using Mixins
Mixins let one "mix-in" certain aspects or
cross-cutting functionality ("abilities") into existing objects.
Any public methods, closures, and properties will be inserted or
overwritten. The objects to be mixed-in need to have a default
(nullary) constructor.
class DrivingAbility {
def drive() { "driving" }
}
class DivingAbility {
def dive() { "diving" }
}
class Vehicle { }
Vehicle.mixin([DrivingAbility.class, DivingAbility.class])
def vehicle = new Vehicle()
vehicle.drive()
vehicle.dive()
Alternatively, the @Mixin annotation can be
used:
@Mixin([DrivingAbility.class, DivingAbility.class])
class Vehicle { }
While the @Mixin annotation causes
the methods to be compiled into the target class by an AST
Transformation, the mixin method causes runtime
changes only.
Missing explicit documentation on these
different concepts may be regarded a flaw. Many Groovy programmers
will rely on what Groovy "auto-magically" produces, however lose
control of the exact results.
Groovy Performance
Although Groovy code are compiled to Java bytecode, Groovy's
performance decreases
significantly compared to Java, in
almost any case. See
a list of benchmarks for that
matter.
As sidenotes, Groovy, normally, performs better
than pure scripting languages like Ruby or Python. - Groovy,
apparently, has been optimized quite a lot over the past years, so
older benchmarks may be outdated. - The Grails framework, that
builds up on Groovy, (optionally) makes use of aggressive caching
at different levels.
Groovy code that under the hood just encapsulates Java code
(as being the case with Groovy's File object) usually
performs most closely to Java.
Numeric operations are usually slow in Groovy because
primitives are employed as their respective
wrappers in Groovy, and thus there's a lot of boxing and
unboxing. There's no workaround for this kind of problem, so
intense numeric operations should be externalized into Java
code.
A method invocation in Groovy consists
usually of several normal method calls, where the arguments are
stored in a array, the classes of the arguments must be retrieved,
a key is generated out of them, a hashmap is used to lookup the
method and if that fails, then we have to test the available
methods for compatible methods, select one of the methods based on
the runtime type, create a key for the hasmap and then in the end,
do a reflection like call on the method.
While that understanding may apply more accurately or less
;-), Groovy employs that costly invocation strategy even in those
cases where, from a primary point of view, invocation based on
statically compiled information would perform much better.
Groovy++
Groovy++ is an extension to Groovy that joins
into the Groovy compilation process to
statically compile
Groovy code. "Static compilation", at this place, means to produce
Java bytecode as one would expect, and not the whole bunch of
invocation calls needed for
runtime meta programming.
This behavior can be achieved by simply applying Groovy++'
@Typed annotation at the package, type, constructor,
or method level.
The @Typed annotation takes two optional
parameters: the boolean debug parameter and the
values parameter of type TypePolicy,
which is an enum of TypePolicy.STATIC
(default), TypePolicy.DYNAMIC, and
TypePolicy.MIXED. MIXED means to compile
statically wherever possible.
Interestingly, Groovy++ joins in to the
compiler's
AST (Abstract Syntax Tree) Transformation
at the latest possible phase, just before bytecode generation. That
means, it has to
visit the whole number of (meta
programming) instructions that the Groovy compiler has generated
until then, and to convert them
back to "pure Java"
instructions.
Impacts on Groovy Programming Paradigms
While compile-time metaprogramming (obviously) is not
affected by Groovy++' static compilation, portions of runtime
metaprogramming are (actually, some constructs get recognized
and statically compiled while others aren't - which is somewhat
confusing).
With partial static compilation, however, there's a breach of
Groovy's paradigm to select overloaded methods by the runtime type
of the references to be passed as arguments, whereas in statically
compiled Java, always the static type decides (amongst other
scopes, this may concern
operator
overloading and
multimethods). -
Code within the same application may behave different - whether
compiled statically or dynamically.
With nested
closures compiled
statically, the implicit variable
owner is not
available (while the Groovy compiler, even in combination with
Groovy++, does not throws an error because of that).
There may be many more issues ...
An additional functionality at its credits, Groovy++ does not
only compile annotated code statically, but also uses complex
algorithms to infer type information to untyped variables
or method return values (type inference). In consequence,
compile-time type checks become available to to a large extend,
even on actually untyped code.
Installing and Using Groovy++
The current Groovy++ 0.2.25 distribution ships
with an - apparently patched - Groovy 1.7.5, but will cause basic
errors to be thrown when used with Groovy 1.7.5, which, at the time
of writing, is the latest stable download at
Codehaus, or
when used with SpringSource Tool Suite 2.5.0 (as well shipping with
Groovy 1.7.5).
To use Groovy++ (considering the @Typed
annotation, only), groovypp-*.jar needs to be on the
classpath.
The Groovy++ distribution contains the Groovy binaries, so
groovyConsole etc. can be used from there.
In IDEs, groovypp-*.jar can be simply added to a
project's build path.
As a side effect, Eclipse, unfortunately,
fails to generate the
Run and
Debug menus for
Groovy classes and scripts. - Nevertheless, compilation works as
expected (as can be seen using a
Java
decompiler).
Java SE 7's invokedynamic
Java SE 7, which is expected to be released in 2011, will
support dynamically typed languages (
JSR
292) through
invokedynamic bytecode. This will
speed up method invocation of dynamically
typed languages on the JVM, like Groovy, JRuby, and Jython.
However, there are additional performance bottlenecks (in
Groovy, specifically) - selecting a matching target object and
method, and preparing the method call (i.e.,
constructing an arguments list) -, which
are not affected by
invokedynamic.
It
has been estimated that Groovy's
metaprogramming code would need to be optimized to take full
advantage of the new JVM features.
Environmental Considerations
Quality
Architecture
Groovy is a language that directly extends, and builds upon,
Java. Groovy adds an expressive syntax, "bonus" constructs like
closures or named parameters, plus - to a large extent - meta
programming capabilities (which makes it also quite slow in
performance). Its dynamic typing doesn't make use of type
inference, and by not supporting generic types, there's an even
greater loss of type information when compared to Java ... or
Scala.
Scala, in contrast to Groovy, is a clean room implementation of
a programming language and is commonly regarded a more consistent
and advanced language. Even James Strachan, original creator of
Groovy, had admitted:
I can honestly say if someone had shown me
the Programming in Scala book [...] back in 2003 I'd probably have
never created Groovy.
The aforementioned quote had become more popular
than Strachan wanted to mean. ;-) See Strachan's
follow-up on that issue.
That is not to say that Scala was superior in
every concern. As Groovy can be regarded a superset of the Java
language, it ought to be quite familiar to Java programmers, and
easy to learn. Scala might be "too expressive" even, and is also
focussing on functional programming, which may not be desired.
Another important aspect is framework support, which is certainly
much better for Groovy.
Code Quality
Groovy's internal code mostly adheres to best practices
regarding design patterns, coding conventions, and
documentation.
Documentation
Groovy's own documentation is impressingly
comprehensive and mostly up-to-date. Nevertheless, it's a bit
scattered, i.e., there is no single page, or print, version, and
there are no learning trails.
There are lots of external articles on Groovy; however, most
of them discuss more specialized aspects than "the whole".
The quantity and quality of
printed
publications is satisfying; however, slightly outdated, in
general. The second edition of
Groovy in Action, for
instance, had been published as a PDF-only "early access" edition
in 2009, and is expected to be finished not before 2011.
Overall, there is plenty of information available, that,
however, has to be made out at times.
Personally, I'm missing a normative
reference like those that, for example, Spring Core,
Hibernate, or any JSRs (Java Specification Requests) provide.
Support
Most public support is provided through
Groovy's mailing
list. A big plus, most supporters there are competent on the
issues, cooperative and helpful, many of them being the Groovy
developers theirselves.
There are a few external native-language discussion boards, as
well.
As can be seen at the
Groovy issue tracker, not every bug is
handled properly and in time. Some open bugs may be "marker
issues", though.
Moreover, the kind of many issues point up a
certain sensivity of the Groovy "magic".
Institutional Backing
Most Groovy core developers are around for several years now,
and they're providing the impression of being quite engaged. It is,
however, hard to find out to which companies, or institutions, they
belong.
According to the Groovy main website, it is acknowledged
SpringSource that "sustains and leads" Groovy
development. SpringSource is a - strategically important -
subsidiary of VMWare, and owner of the
Spring framework
and the Spring- and Groovy-based
Grails framework.
Related Libraries, Modules, and Frameworks
Groovy comes with built-in libraries, faciliating common
programming tasks, including the scopes of:
- Database programming
- XML processing
- File I/O
- Threads and processes
- Swing GUIs
- ANT tasks
- XML or HTML
- Unit testing
There are also a number of
Groovy Modules available, one of which is
Griffon, a Grails-like framework for developing
desktop applications.
Beyond Groovy's own libraries, the most important project
leveraging Groovy ought to be
Grails.
Grails is a Ruby on
Rails-like web framework, led by
SpringSource, written in, and leveraging,
Groovy. Like Ruby on Rails, Grails features
convention over
configuration and, as such, is very easy to use.
Towards the database, Grails uses the
GORM
object/relational mapper, which is both based on Groovy and
Hibernate. Towards the presentation layer, Grails employs a MVC
(Model/View/Controller) architecture, part of which are Groovy
Server Pages (GSP). It's never been so simple to write custom
taglibs or plugins!
Popularity
In terms of quantitative usage relative to other programming
languages, Groovy is a niche language. The
TIOBE Index states that Groovy's
popularity amongst programming languages ranks lower than
0.2%.
According to TIOBE, there had been an increased interest in
dynamically typed languages, which currently appears to calm down
again:
In Groovy, typing is
optional. List items and map values, however,
are always untyped.
According to
Indeed.com, there are significantly more
job offerings applying to Ruby programming than for Groovy, both
steadily increasing:
Demand on Groovy skills, however, has a larger growth:
Most conversations related to Groovy (including end user
support and developers communication) are handled by
Groovy's mailing
lists. There appears to be a peak of traffic around 2007, and a
current decrease:
It also makes sense to compare absolute and relative
indicators of
Ruby,
JRuby, or
Grails
As the term "groovy" is also an adjective, it isn't likely to
find
Google Trends on that term that are not
scattered. Nevertheless, there are
trends for related web frameworks. According to those, Grails
is not as popular as Ruby on Rails (which, apparently, had
experienced quite a hype); however, Grails has been enjoying a
relatively constant rate of interest over the last time:
A young language, Groovy has gained quite a lot attention. To
estimate whether further investigation might pay, several
indicators should be taken into account:
- the Java environment, in general: After the
Oracle/Sun buy-out has been completed, finally,
there are several signs for Java newly re-strengthening.
- the impact of alternative languages in the
Java context: Scala might be the better language
(had even James Strachan, Groovy's original creator, admitted), but with Grails and some other
projects, Groovy is better supported framework-wise. - JRuby and
JPython are not as well integrated into Java's programming
paradigms as Groovy and Scala. - Java continually
evolves, but is regarded as starving by many. (Closures are on the
roadmap for Java 8, expected in 2012.)
There's also Google Trends information on different languages in the Java
context.
- as for Ruby on Rails vs. Grails: According to
Google Trends, Grails has never perceived
such a booming (and next, falling) perception than RoR. Besides of
RoR being quite the first RAD web framework, one could interpret
that Groovy's binding to the JVM was limiting on the one hand, but
granting for robustness, reliability, extensibility, and finally,
practicability, on the other.
- the number and and progression of (possibly printed)
publications: After an initial boom around year
2005, and the last higher peak in 2007, the number
of new publications appears to have decreased slightly.
Nevertheless, there's plenty of up-to-date information
available.
Although Groovy's own documentation does not provide a central
thread (it even doesn't allow for to be printed in one piece), it
is quite comprehensive. It's mostly up-to-date but not
everywhere.
- the progression of new software releases: New
Groovy versions appear to be released on a steady basis.
- Groovy's code quality: Groovy's internal code
adheres to best practices regarding design patterns, coding
conventions, and documentation.
- quantity and quality of support facilities:
There are few (external) support forums, only. Support is mostly
provided by Groovy's mailing lists. Traffic there may be low, but
one can find competent and engaged contacts. Commercial training is
available.
- the progression of dependant frameworks and
the backing by partners: Groovy is intensely used
by the Grails framework, which is continually developed by
SpringSource (a subsidiary of VMWare). While the
Spring Framework has partially lost attention over the last one or
two years (partly due to the extraordinary improvements of Java EE
6), it's backed and evolved by VMWare which have great plans with
that regarding virtualization and Cloud platforms. While Grails is
not a SpringSource's primary framework (they even appear to feature
Spring Roo more for some reason), it well integrated and still
quite central in philosophy.
Extensions to Groovy, such as plugins and
Groovy++, are actively developed by third
parties.
Evaluation and Conclusion
Basics
Optional (and weak)
typing (even
missing type inference) may suit well
with rapid prototyping but bears the risk of getting runtime errors
because of
missing compile-time checks. Additionally,
readability, and thus, maintainability, may
decrease.
The
simple data
types in Java (as well as arrays) - although a breach of the
object-orientated paradigm - have their sense in respect to
operational performance. It ought to be possible to
change the
way the JVM works under the hood; however, there's always been
Java's policy to
never breach backwards
compatibility.
When refraining from using simple data types, Groovy sacrifices
performance in favor of
best
practices.
Closures
Closures are vastly
superior to Java's anonymous inner classes, and ought to be
considered
standard constructs in any modern imperative
programming language.
Literals
Groovy literals -
GStrings, and Groovy's central
native syntax for collections - are of
major assistance for developers, being
well readable and
sparing much plumbing code.
Regular expressions are
first-class citizens in Groovy, being particularly useful for
pattern matching on strings, and
collection
filtering and transformation.
Control Structures
The "
Groovy
Truth" - being able to evaluate any object in conditional
statements - and the
enhanced
switch statement overcome Java
limitations that are almost solely due to JVM issues. Impacts
on performance ought to be minimal, and readability ain't
sacrificed.
Optional return values might
lead to confusion in some
cases, but mostly don't. -
Java's concept of checked
exceptions had been meant well,
but proved to be
tedious and error-prone in
practice.
There is Groovy's focus on
assertions, which
improve clarity, and thus,
maintainability, and enforce logical checks at runtime.
Assertions as a central design pattern, on the other hand, point up
Groovy's intrinsic problems arising from potentially
missing
compile-time type checks and
dynamic (method)
invocation.
Object Orientation
Meta Programming
Compile-time meta programming
allows for implementing composite objects through
delegates, and AOP-style (Aspect Orientated
Programming) introduction of cross-cutting behaviour through
categories,
mixins, and
interceptors. While
any of these methods use different design patterns / APIs,
implementation in each case is
simple, considering the
power of these constructs.
Performance
While Groovy's
performance may be better than that of
scripting languages,
performance significantly decreases
when compared to statically compiled Java code.
Programming Environment
IDE tooling support is naturally weak with
dynamic languages like Groovy. IntelliJ IDEA engages in extra
efforts, i.e., regarding code completion, using type inference and
supporting selected APIs (like the
Default Groovy Methods).
In general, missing compile-time checks urge the programmer to
write more unit tests.
Conclusion
Groovy adds many convenient and/or powerful features to the
Java language, of which closures (and their use with collections),
the default Groovy methods, and metaprogramming capabilities, ought
to be considered most weighty.
A dynamic language, Groovy suites well for more agile software
projects, however might introduce issues regarding scalability and
maintainability.
Coming from Java, Groovy is easy to learn. Many Groovy
programmers are quite enthusiastic about Groovy capabilities in the
beginning, but at a later stage realize Groovy's somewhat "hacked"
language design and, in general, disadvantages of dynamically typed
languages.
Resources
All links retrieved at the date of
publication.
On Groovy
- Codehaus Foundation (Edt.): Groovy Documentation, groovy.codehaus.org,
2011
- Glover, Andrew: alt.lang.jre: Feeling Groovy,
www.ibm.com/developerworks, 2004
- König, Dierk; Andrew Glover, Paul King, Guillaume Laforge, Jon
Skeet: Groovy in Action, Manning, 2007
- König, Dierk; Guillaume Laforge, Paul King, Jon Skeet: Groovy in
Action, Second Edition, Manning, 2009/2011 (to be printed; currently available as PDF)
- Rocher, Graeme & Brown, Jeff: The Definitive Guide to
Grails, Second Edition, Apress, 2009
(Appendix: The Groovy Language)
- Strachan, James, et al.: JSR 241:
The Groovy Programming Language, jcp.org, 2004
- Staudemeyer, Jörg: Groovy für Java-Entwickler, O'Reilly, 2007
(in German; comprehensive and
gratis)
Inside Groovy
- Codehaus Foundation (Edt.): DefaultGroovyMethods ApiDoc,
groovy.codehaus.org, 2011
- Codehaus Foundation (Edt.): Groovy JDK ApiDocs, groovy.codehaus.org,
2011
- Dawrani, Rochan: A sneak peek into Groovy++ - What is it? Why
is it there?, groovy.dzone.com, 2010
- Dupuy, Emmanuel: JD | Java Decompiler,
java.decompiler.free.fr, 2011
- Oracle, Inc. (Edt.): java.util.regex.Pattern ApiDoc,
download-llnw.oracle.com, 2011
- Panasyuk, Andrei: Groovy performance,
rocketscience.itteco.org, 2010
- Tkachman, Alex, et al.: Groovy++ (Groovy Booster), code.google.com,
2011
Beyond Groovy
- Abdul-Jawad, Bashar: Groovy and Grails Recipes,
Apress, 2009
- Codehaus Foundation (Edt.): Groovy - Modules, docs.codehaus.org, 2011
- Rocher, Graeme & Brown, Jeff: The Definitive Guide to
Grails, Second Edition, Apress, 2009
- Rose, John, et al.: JSR 292:
Supporting Dynamically Typed Languages on the JavaTM Platform,
jcp.org, 2011
- SpringSource (Edt.): Grails - The Search is over., grails.org,
2011
- Söding, Robert: A Comprehensive Introduction into the Spring
Framework (with Sample Application), metagear.de, 2009
- Söding, Robert: Solutions to the Exercises in the "Scala By Example"
Manual, metagear.de, 2011
- Söding, Robert: Unified Expression Language (EL),
metagear.de, 2009
- Strachan, James: Scala as the long term replacement for
java/javac?, macstrac.blogspot.com, 2009