This appendix reviews the basics of the Groovy programming language. The Gradle build files consist largely of a Domain Specific Language, written in Groovy, for builds. In addition to the DSL, any legal Groovy code can be added to the build.
Groovy is a general-purpose programming language, based on Java, that compiles to Java byte codes. While it has functional capabilities, it is an object-oriented language that is arguably the next-generation language in the path from C++ to Java.
The “Hello, World!” program for Groovy is the one-liner shown in Example A-1.
println
'Hello, World!'
Items of note:
Semicolons are optional. If you add them, they work, but they’re not required.
Parentheses are optional until they’re not. If the compiler guesses correctly where they should have gone, everything works. Otherwise, add them back in. The println
method takes a String
argument. Here the parentheses are left out.
There are two types of strings in Groovy: single-quoted strings, like Hello, are instances of java.lang.String
. Double-quoted strings are Groovy strings and allow interpolation, shown in Example A-2.
There are no “primitives” in Groovy. All variables use the wrapper classes, like java.lang.Integer
, java.lang.Character
, and java.lang.Double
. The native data type for integer literals, like 3, is Integer
. The native data type for floating point literals, like 3.5, is java.math.BigDecimal
.
assert
3
.
class
=
=
Integer
assert
(
3.5
)
.
class
=
=
BigDecimal
assert
'abc'
instanceof
String
assert
"abc"
instanceof
String
String
name
=
'Dolly'
assert
"Hello, ${name}!"
=
=
'Hello, Dolly!'
assert
"Hello, $name!"
=
=
'Hello, Dolly!'
assert
"Hello, $name!"
instanceof
GString
Note that you can invoke methods on literals, because they are instances of the wrapper classes.
Groovy lets you declare variables with either an actual type, like String
, Date
, or Employee
, or you can use def
. See Example A-3.
Integer
n
=
3
Date
now
=
new
Date
()
def
x
=
3
assert
x
.
class
==
Integer
x
=
'abc'
assert
x
.
class
==
String
x
=
new
Date
()
assert
x
.
class
==
Date
Java imports the java.lang
package automatically. In Groovy, the following packages are all automatically imported:
java.lang
java.util
java.io
java.net
groovy.lang
groovy.util
The classes java.math.BigInteger
and java.math.BigDecimal
are also available without an import statement.
The assert
method in Groovy evaluates its argument according to the “Groovy Truth.” That means:
Nonzero numbers (positive and negative) are true
Nonempty collections, including strings, are true
Nonnull references are true
Boolean true
is true
The Groovy Truth is illustrated in Example A-4.
assert
3
;
assert
-
1
;
assert
!
0
assert
'abc'
;
assert
!
''
;
assert
!
""
assert
[
3
,
1
,
4
,
1
,
5
,
9
]
assert
![]
Asserts that pass return nothing. Asserts that fail throw an exception, as in Example A-5, with lots of debugging information included.
int
x
=
5
;
int
y
=
7
assert
12
==
x
+
y
// passes
assert
12
==
3
*
x
+
4.5
*
y
/ (2/
x
+
y
**
3
)
// fails
The result of the failing assertion is shown in Example A-6.
Exception thrown Assertion failed: assert 12 == 3 * x + 4.5 * y / (2/x + y**3) | | | | | | | || | || false| 5 | | 7 | |5 | |343 15 | 31.5| 0.4| 7 | | 343.4 | 0.0917297612 15.0917297612 at ConsoleScript11.run(ConsoleScript11:4)
In Groovy, every operator corresponds to a method call. For example, the +
sign invokes the plus
method on Number
. This is used extensively in the Groovy libraries. Some examples are shown in Example A-7.
assert
3
+
4
==
3
.
plus
(
4
)
assert
3
*
4
==
3
.
multiply
(
4
)
assert
2
**
6
==
64
assert
2
**
6
==
2
.
power
(
6
)
assert
'abc'
*
3
==
'abcabcabc'
// String.multiply(Number)
try
{
3
*
'abc'
}
catch
(
MissingMethodException
e
)
{
// no Number.multiply(String) method
}
String
s
=
'this is a string'
assert
s
+
' and more'
==
'this is a string and more'
assert
s
-
'is'
==
'th is a string'
assert
s
-
'is'
-
'is'
==
'th a string'
Date
now
=
new
Date
()
Date
tomorrow
=
now
+
1
// Date.plus(Integer)
assert
tomorrow
-
1
==
now
// Date.minus(Integer)
Groovy has an exponentiation operator, **
, as shown.
In Java, the ==
operator checks that two references are assigned to the same object. In Groovy, ==
invokes the equals
method, so it checks for equivalence rather than equality. If you want to check references, use the is
method.
Groovy has native syntax for collections. Use square brackets and separate values by commas to create an ArrayList
. You can use the as
operator to convert one collection type to another. Collections also have operator overloading, implementing methods like plus
, minus
, and multiply
(Example A-8).
def
nums
=
[
3
,
1
,
4
,
1
,
5
,
9
,
2
,
6
,
5
]
assert
nums
instanceof
ArrayList
Set
uniques
=
nums
as
Set
assert
uniques
==
[
3
,
1
,
4
,
5
,
9
,
2
,
6
]
as
Set
def
sorted
=
nums
as
SortedSet
assert
sorted
==
[
1
,
2
,
3
,
4
,
5
,
6
,
9
]
as
SortedSet
assert
sorted
instanceof
TreeSet
assert
nums
[
0
]
==
3
assert
nums
[
1
]
==
1
assert
nums
[-
1
]
==
5
// end of list
assert
nums
[-
2
]
==
6
assert
nums
[
0
..
3
]
==
[
3
,
1
,
4
,
1
]
// two dots is a Range
assert
nums
[-
3
..-
1
]
==
[
2
,
6
,
5
]
assert
nums
[-
1
..-
3
]
==
[
5
,
6
,
2
]
String
hello
=
'hello'
assert
'olleh'
==
hello
[-
1
..
0
]
// Strings are collections too
A Range
in Groovy consists of two values separated by a pair of dots, as in from..to
. The range expands starting at the from
position, invoking next
on each element until it reaches the to
position, inclusive.
Maps use a colon notation to separate the keys from the values. The square bracket operator on a map is the getAt
or putAt
method, depending on whether you are accessing or adding a value. The dot operator is overloaded similarly. See Example A-9 for details.
Groovy has a class called Closure
that represents a block of code that can be used like an object. Think of it as the body of an anonymous method, which is an oversimplification but not a bad start.
A closure is like a Java 8 lambda, in that it takes arguments and evaluates a block of code. Groovy closures can modify variables defined outside them, however, and Java 8 does not have a class called Lambda
.
Many methods in Groovy take closures as arguments. For example, the each
method on collections supplies each element to a closure, which is evaluated with it. An example is in Example A-10.
def
nums
=
[
3
,
1
,
4
,
1
,
5
,
9
]
def
doubles
=
[
]
nums
.
each
{
n
-
>
doubles
<
<
n
*
2
}
assert
doubles
=
=
[
6
,
2
,
8
,
2
,
10
,
18
]
Modifying a variable defined outside a closure is considered a side-effect, and not good practice. The collect
method, discussed later, is preferred.
This is a natural way to double the values in a list, but there is a better alternative, called collect
. The collect
method transforms a collection into a new one by applying a closure to each element. It is similar to the map
method from Java 8, or just think of it as the map
operation in a map-filter-reduce process (Example A-11).
def
nums
=
[
3
,
1
,
4
,
1
,
5
,
9
]
def
doubles
==
nums
.
collect
{
it
*
2
}
assert
doubles
==
[
6
,
2
,
8
,
2
,
10
,
18
]
When a closure has a single argument (which is the default), and you don’t give that argument a name using the arrow operator, the dummy name defaults to the word it
. In this case, the collect
method creates the doubles
collection by applying it * 2
in a closure to each element.
Java classes with just attributes and getters and setters are often called Plain Old Java Objects, or POJOs. Groovy has similar classes called POGOs. An example is in Example A-12.
import
groovy.transform.Canonical
@Canonical
class
Event
{
String
name
Date
when
int
priority
}
This little class actually has a lot of power. For a POGO:
The class is public
by default
Attributes are private
by default
Methods are public
by default
Getter and setter methods are generated for each attribute not marked public
or private
Both a default constructor and a “map-based” constructor (uses arguments of the form “attribute:value”) are provided
In addition, this POGO include the @Canonical
annotation, which triggers an Abstract Syntax Tree (AST) transformation. AST transformations modify the syntax tree created by the compiler during the compilation process in specific ways.
The @Canonical
annotation is actually a shortcut for three other AST transformations: @ToString
, @EqualsAndHashCode
, and @TupleConstructor
. Each does what they sound like, so in this case, the @Canonical
annotation adds to this class:
A toString
override that displays the fully-qualified name of the class, followed by the values of the attributes, in order from top down
An equals
override that does a null-safe check for equivalence on each attribute
A hashCode
override that generates an integer based on the values of the attributes in a fashion similar to that laid out by Joshua Bloch in his Effective Java (Addison-Wesley) book long ago
An additional constructor that takes the attributes as arguments, in order
That’s a lot of productivity for seven lines of code. Example A-13 shows how to use it.
Event
e1
=
new
Event
(
name:
'Android Studio 1.0'
,
when:
Date
.
parse
(
'MMM dd, yyyy'
,
'Dec 8, 2014'
),
priority:
1
)
Event
e2
=
new
Event
(
name:
'Android Studio 1.0'
,
when:
Date
.
parse
(
'MMM dd, yyyy'
,
'Dec 8, 2014'
),
priority:
1
)
assert
e1
.
toString
()
==
'Event(Android Studio 1.0, Mon Dec 08 00:00:00 EST 2014, 1)'
assert
e1
==
e2
Set
events
=
[
e1
,
e2
]
assert
events
.
size
()
==
1
Gradle uses all these features, and more, but this summary should get you started.
Gradle build files support all Groovy syntax. Here are few specific examples, however, that illustrate Groovy in Gradle.
In Example A-14, the word apply
is a method on the Project
instance. The parentheses on the method are optional, and left out here. The argument is setting a property called plugin
on the Project
instance to the string value supplied.
apply
plugin:
'com.android.application'
In Example A-15, the term android
is part of the plug-in’s DSL, which takes a closure as an argument. Properties inside the closure, like compileSdkVersion
, are method calls with optional parentheses. In some Gradle build files, properties are assigned using =
, which would invoke a corresponding setter method. The developers of the Android plug-in frequently added a regular method, like compileSdkVersion(23)
, in addition to the setter, setCompileSdkVersion(23)
.
android
{
compileSdkVersion
23
buildToolsVersion
"23.0.1"
}
Also, “nested” properties, like compileSdkVersion
here, can be set using a dot notation as an alternative:
android.compileSdkVersion = 23
Both are equivalent.
Recent versions of the plug-in add a clean
task to the Gradle build file. This task has name called clean
, is an instance of the Delete
class (as subclass of Task
), and takes a closure as an argument. In keeping with standard Groovy practice, the closure is shown after the parentheses (Example A-16).
task
clean
(
type:
Delete
)
{
delete
rootProject
.
buildDir
}
If a Groovy method takes a Closure
as its last argument, the closure is normally added after the parentheses.
The implementation here invokes the delete
method (again, with optional parentheses) on the rootProject.buildDir
. The value of the rootProject
property is the top-level project, and the default value of buildDir
is “build,” so this task deletes the “build” directory in the top-level project.
Note that calling clean
in the top-level project will also invoke it on the app
subproject, which will delete the build directory there as well.
In Example A-17, the compile
term is part of the DSL, implying that its argument is applied during the compile
phase. The fileTree
method is shown with parentheses, though they could be left out. The dir
argument takes a string representing a local directory. The include
argument takes a Groovy list (the square brackets) of file patterns.
dependencies
{
compile
fileTree
(
dir:
'libs'
,
include:
[
'*.jar'
])
}
The book Making Java Groovy, by Ken Kousen (Manning), discusses Groovy and integrates it with Java, and also has a chapter on build processes with Gradle. The definitive reference for Groovy is Groovy in Action, Second Edition, by Dierk Konig, Paul King, et al. (Manning).
The Groovy home page is at http://groovy-lang.org, and contains extensive documentation.
O’Reilly also has three video courses on Groovy: Groovy Programming Fundamentals, Practical Groovy Programming, and Mastering Groovy Programming. All three are available on Safari as well.
52.15.129.90