The GDK (Groovy Development Kit) provides a number of helper methods, operators, utilities, and additional classes.
Some of these are methods added to every Java class, like "each", and some are more obscure.
Collections
Groovy adds tons of helpful
methods that allow easier manipulation of collections, arrays, or any
Iterable:
sort—Sorts the collection (if it is sortable).
findAll—Finds all elements that match a closure.
collect—An iterator that builds a new collection.
inject—Loops through the values and returns a single value (similar to the concept of “reduce”).
each—Iterates through the values using the given closure.
eachWithIndex—Iterates through with two parameters: a value and an index.
find—Finds the first element that returns true when passed to a given a closure.
findIndexOf—Finds the first element that matches a closure and returns its index.
any—True if any element returns true for the closure (like OR).
every—True if all elements return true for the closure (like AND).
reverse—Reverses the ordering of elements in a list.
first—Gets the first element of a list.
last—Returns the last element of a list.
tail—Returns all elements except the first element of a list (useful for, e.g., recursive strategies).
Java 8 Streams
Groovy adds support to simplify dealing with Java 8+ Streams as well. For example, toList() and toSet() are added to the java.util.stream.Stream<T> interface allowing you to shortcut collect(Collectors.toList()) and collect(Collectors.toSet()), respectively.
Spread
The
spread operator
can be used to access the property of every element in a collection. It can be used instead of
collect in many cases. For example, let’s take a simple Dragon class defined as follows:
class Dragon { String name }
You could print the name of every Dragon in a list named
dragons:
1 dragons*.name.each { println it }
This is a shorter form for the following:
1 dragons.collect { dragon -> d.name }.each { println it }
GPath
GPath
is something like XPath in Groovy. Thanks to the support of property notation for both lists and maps, Groovy provides syntactic sugar, making it really easy to deal with nested collections, as illustrated in the following examples:
1 def listOfMaps = [['a': 11, 'b': 12], ['a': 21, 'b': 22]]
2 assert listOfMaps.a == [11, 21] //GPath notation
3 assert listOfMaps*.a == [11, 21] //spread dot notation
4
5 listOfMaps = [['a': 11, 'b': 12], ['a': 21, 'b': 22], null]
6 assert listOfMaps*.a == [11, 21, null] // caters for null values
7 assert listOfMaps*.a == listOfMaps.collect { it?.a } //equivalent notation
8 // But this will only collect non-null values
9 assert listOfMaps.a == [11,21]
As demonstrated in the preceding examples, *. provides null values, whereas using only “ . ” skips any null values.
IO
The GDK helps you a lot with input/output (IO).
Files
The GDK adds several methods to the
File class
to ease reading and writing files.
1 println path.toFile().text
A
getText() method
is added to the
File class, which simply reads the whole file.
1 new File("books.txt").text = "Modern Java"
Here we are using the
setText method on the
File class, which simply writes the file contents. For binary files, you can also use the
bytes property on
File:
1 byte[] data = new File('data').bytes
2 new File('out').bytes = data
If you want to use an
InputStream or reader or the corresponding
OutputStream or writer for output, you have the following methods which also handle closing the stream for you:
1 new File('dragons.txt').withInputStream {in -> }
2 new File('dragons.txt').withReader {r -> }
3 new File('dragons.txt').withOutputStream {out ->}
4 new File('dragons.txt').withWriter {w -> }
Lastly you can use the
eachLine method to read each line of a file. For example:
1 new File('dragons.txt').eachLine { line->
2 println "$line"
3 }
4 //OR
5 new File('dragons.txt').eachLine { line, num ->
6 println "Line $num: $line"
7 }
In all of these cases, Groovy takes care of closing the I/O resource even if an exception is thrown.
URLs
The GDK makes it extremely simple to execute a URL.
The following Java code opens an HTTP connection on the given URL (
http://google.com in this case), reads the data into a byte
array, and prints out the resulting text.
1 URL url = new URL("http://google.com");
2 InputStream input = (InputStream) url.getContent();
3 ByteArrayOutputStream out = new ByteArrayOutputStream();
4 int n = 0;
5 byte[] arr = new byte[1024];
6
7 while (-1 != (n = input.read(arr)))
8 out.write(arr, 0, n);
9
10 System.out.println(new String(out.toByteArray()));
However, in Groovy this also can be reduced to one line (leaving out exceptions):
1 println "http://google.com".toURL().text
A toURL() method is added to the String class, and a getText() method (which is called using ".text") is added to the URL class in Groovy.
Ranges
The Range
is a built-in type in Groovy. It can be used to perform loops, in switch cases, extracting substrings, and other places. Ranges are generally defined using the syntax start..end.
Ranges come in handy for traversing using the
each method and
for loops:
1 (1..4).each {print it} //1234
2 for (i in 1..4) print i //1234
A
case statement
was demonstrated in an earlier chapter, such as the following:
switch (x) {
case "foo": result = "foo"
break
case 12..30: result = "12 to 30"
break
You can use ranges to extract substrings from a string using the
getAt syntax. For example:
1 def text = 'learning groovy',
2 println text[0..4] //learn
3 println text[0..4, 8..-1] //learn groovy
You can also use ranges to access elements of a list:
1 def list = ['hank', 'john', 'fred']
2 println list[0..1] //[hank, john]
You can define a
range to be exclusive of the last number by using the
..< operator. For example, another way to print
1234 would be the following:
1 (1..<5).each {print it} //1234
Utilities
The GDK adds several utility classes, such as ConfigSlurper, JsonBuilder, JsonSlurper, Expando, and ObservableList/Map/Set.
ConfigSlurper
ConfigSlurper
is a utility class for reading configuration files defined in the form of Groovy scripts. Like with Java Properties files (files ending with
.properties), ConfigSlurper allows for a dot notation. It also allows for nested (closure) configuration values and arbitrary object types.
1 def config = new ConfigSlurper().parse('''
2 app.date = new Date()
3 app.age = 42
4 app {
5 name = "Test${42}"
6 }
7 ''')
8
9 def properties = config.toProperties()
10
11 assert properties."app.date" instanceof String
12 assert properties."app.age" == '42'
13 assert properties."app.name" == 'Test42'
JsonBuilder and JsonSlurper
Groovy has the
JsonBuilder and
JsonSlurper classes to help deal with JSON (JavaScript Object Notation), a very common data format. They both are in the “groovy.json” package. For example:
import groovy.json.*
def builder = new JsonBuilder()
builder.person {
name 'Adam'
age 37
conferences 'Gr8Conf', 'ÜberConf'
}
println builder
The preceding code demonstrates using the JsonBuilder to build a person object which would result in the following output:
{"person":{"name":"Adam","age":37,"conferences":["Gr8Conf"," ÜberConf"]}
Likewise we can use the JsonSlurper to parse JSON as follows:
def slurper = new JsonSlurper()
def result = slurper.parseText(builder.toString())
assert result.person.name == "Adam"
assert result.person.age == 37
assert result.person.conferences.size() == 2
assert result.person.conferences[0] == "Gr8Conf"
This parses the same JSON from the previously defined builder and verifies that the returned values are as expected. Note that “result” is a map of values, “person” is also a map, and “conferences” is a list in this case.
Expando
The
Expando class
can be used to create a dynamically expandable object. You can add fields and methods. This can be useful when you want to use extremely dynamic meta-programming. For example, see the following code:
1 def expando = new Expando()
2 expando.name = 'Draco' // field value
3 expando.say = { String s -> "${name} says $s" } //method
4 expando.say('hello') // Output: Draco says hello
ObservableList/Map/Set
Groovy comes with observable lists, maps, and sets. Each of these collections triggers PropertyChangeEvent (from the java.beans package) when elements are added, removed, or changed. Note that a PropertyChangeEvent does not only signal that an event has occurred, it also holds information on the property name and the old/new value of a property.
Here’s an example using
ObservableList and printing out the class of each event:
1 def list = new ObservableList()
2 def printer = {e -> println e.class}
3 list.addPropertyChangeListener(printer)
4 list.add 'Harry Potter'
5 list.add 'Hermione Granger'
6 list.remove(0)
7 println list
This would result in the following output:
1 class groovy.util.ObservableList$ElementAddedEvent
2 class java.beans.PropertyChangeEvent
3 class groovy.util.ObservableList$ElementAddedEvent
4 class java.beans.PropertyChangeEvent
5 class groovy.util.ObservableList$ElementRemovedEvent
6 class java.beans.PropertyChangeEvent
7 [Hermione Granger]
This can be useful for using the Observer pattern on collections.