'Groovy for Java developer: learning Groovy-specific features' post illustration

Groovy for Java developer: learning Groovy-specific features

avatar

Groovy is an object oriented dynamic language for the JVM. Dynamically compiled to a bytecode and interoperable with most Java libraries, Groovy is a perfect candidate for writing lightweight scripts that still can benefit from the Java ecosystem. Also, it is really easy to learn since the good old Java syntax is usually valid in Groovy. There are, however, a few caveats that you should be aware of before start coding. This post shows some specific features, and also gives a few suggestions which will help to write a clear, groovy-style code.

Groovy beans

Groovy beans can be declared in a very simple way. A typical Java bean consists of a constructor, private properties, and getter / setter methods. A Groovy bean should contain only fields, whereas getters and setters will be created implicitly:

1
2
3
4
class User {
    String firstName // bean field
    String lastName  // bean field
}
1
2
3
def user = new User()
user.firstName = "John"
user.lastName = "Doe"

Here, the user.firstName and user.lastName assignments are in fact done through the setter call. If needed, you can declare any custom getter and setter for a field, for example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class User {

    String firstName

    String getFirstName() {
        "The first name is $firstName"
    }

    void setFirstName(String firstName) {
        this.firstName = "[$firstName]"
    }

    // ...
}
1
2
user.firstName = 'John'
println user.firstName

This results in a string which contains the brackets added by the setter and the "The first name is " prefix from the getter:

1
The first name is [John]

If you provide an access modifier for a field, you will just create a field, the getter / setter will not be added.

Groovy closures

Groovy offers a way to create functions that are first class objects - Groovy closures. Using the closures, you can define a code block and then pass it around as if it is a regular variable:

1
2
3
4
5
6
7
8
9
10
11
// the simplest closure
def hello = {"Hello, $it!"}
assert hello('Chris') == 'Hello, Chris!'

// the closure that do not take any params
def bye = {->'Bye!'}
assert bye() == 'Bye!'

// the closure with several params
def niceHello = {firstName, lastName -> "Hello, $firstName $lastName!"}
assert niceHello('Chris', 'Bennett') == 'Hello, Chris Bennett!'

As you can see, a closure takes one it parameter by default, you can change this behaviour by specifying named parameters, or omitting parameters altogether with {-> ...} declaration.

Groovy lists and maps

Groovy provides a build-in support for Lists and Maps, and allows to initialize and manipulate the data structures in a way very similar to JavaScript. At first, let's have a look at the List implementation, you can create an empty list just be declaring two square brackets:

1
2
3
4
5
// an empty list
def emptyList = []

// predefined list
def list = ['One', 'Two', 'Three']

Or get an element or the range of elements from a list:

1
2
3
4
5
6
7
8
9
10
def list = ['One', 'Two', 'Three']

// gets the first element from the list
assert list[0] == 'One'

// gets a range of elements from the list
assert list[1..2] == ['Two', 'Three']

// gets another range
assert list[-1..-2] == ['Three', 'Two']

Also, there are available several handy operations for iterating and transforming the lists:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// iterates the list
def emptyList = []
list.each {emptyList << "$it!"}
assert emptyList == ['3!', '1!', '2!']

// iterates the list and transforms each entry into a new value
// using the closure
assert list.collect {it * 2} == [6, 2, 4]

// sorts using the closure as a comparator
assert list.sort {it1, it2 -> it1 <=> it2} == [1, 2, 3]

// gets min or max using closure as comparator
assert list.min {it1, it2 -> it1 <=> it2} == 1

The Map implementation has a lot of syntactic sugar as well. An empty map can be declared with two square brackets and a colon:

1
2
3
4
5
// an empty map
def emptyMap = [:]

// predefined map
def map = [John: 10, Mark: 20, Peter: 'Not defined']

You can get a value from a Map using two styles — the bean style or the associated array style:

1
2
3
4
5
6
7
8
9
10
11
def map = [John: 10, Mark: 20, Peter: 'Not defined']

// the array style
assert map['Peter'] == 'Not defined'

// the bean style
assert map.Mark == 20

// also you can preset default value that will be returned by
// the get method if key does not exist
assert map.get('Michael', 100) == 100

And perform iterating and transforming with the help of closures:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// iterates the map
def emptyMap = [:]
def map = [John: 10, Mark: 20, Peter: 'Not defined']
map.each { key, value ->
    emptyMap.put key, "$key: $value" as String
}
assert emptyMap == [John: 'John: 10', Mark: 'Mark: 20', 
        Peter: 'Peter: Not defined']

// iterates the map and transforms each entry using
// the closure, returns a list of transformed values
assert map.collect { key, value ->
    "$key: $value"
} == ['John: 10', 'Mark: 20', 'Peter: Not defined']

// sorts map elements using the closure as a comparator
map.put 'Chris', 15
assert map.sort { e1, e2 ->
    e1.key <=> e2.key
} == [John: 10, Chris: 15, Mark: 20, Peter: 'Not defined']

Other helpful constructs

  • boolean operator == works the same as Java equals for all types. Unlike Java where == means equality for primitive types and identity for objects. To compare by identity, use the is method instead:
1
2
assert [] == []
assert ![].is([])
  • the .* operator is introduced to perform operations on all the members of a collection:
1
assert [1, 2, 3] == ['a', 'ab', 'abc']*.size()
  • the return keyword is optional at the end of the methods:
1
2
3
4
5
6
7
8
9
def foo() {
    // do something
    return something
}
// is equals to
def bar() {
    // do something
    something
}

Conclusion

There are a lot of other interesting features: metaprogramming, functional programming techniques, AST transformations, you will surely discover them all along the way. The power of Groovy is that you can start with a basic Java syntax, and then gradually learn how to improve the code until it becomes truly fluent and dynamic.

If you're looking for a developer or considering starting a new project,
we are always ready to help!