[proxy] web.archive.org← back | site home | direct (HTTPS) ↗ | proxy home | ◑ dark◐ light

The Apache Groovy programming language

8.1. Spread operator

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

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

The expression cars*.make is equivalent to cars.collect{ it.make }. Groovy’s GPath notation allows a short-cut when the referenced property isn’t a property of the containing list, in that case it is automatically spread. In the previously mentioned case, the expression cars.make can be used, though retaining the explicit spread-dot operator is often recommended.

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

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

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

class Component {
    Long id
    String name
}
class CompositeObject implements Iterable<Component> {
    def components = [
        new Component(id: 1, name: 'Foo'),
        new Component(id: 2, name: 'Bar')]

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

Use multiple invocations of the spread-dot operator (here cars*.models*.name) when working with aggregates of data structures which themselves contain aggregates:

class Make {
    String name
    List<Model> models
}

@Canonical
class Model {
    String name
}

def cars = [
    new Make(name: 'Peugeot',
             models: [new Model('408'), new Model('508')]),
    new Make(name: 'Renault',
             models: [new Model('Clio'), new Model('Captur')])
]

def makes = cars*.name
assert makes == ['Peugeot', 'Renault']

def models = cars*.models*.name
assert models == [['408', '508'], ['Clio', 'Captur']]
assert models.sum() == ['408', '508', 'Clio', 'Captur'] // flatten one level
assert models.flatten() == ['408', '508', 'Clio', 'Captur'] // flatten all levels (one in this case)

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

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

8.1.1. Spreading method arguments

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

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

then if you have the following list:

def args = [4,5,6]

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

assert function(*args) == 26

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

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

8.1.2. Spread list elements

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

def items = [4,5]                      (1)
def list = [1,2,3,*items,6]            (2)
assert list == [1,2,3,4,5,6]           (3)
1 items is a list
2 we want to insert the contents of the items list directly into list without having to call addAll
3 the contents of items has been inlined into list

8.1.3. Spread map elements

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

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

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

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

8.2. Range operator

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

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

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

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

8.3. Spaceship operator

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

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

8.4. Subscript operator

The subscript operator is a short hand notation for getAt or putAt, depending on whether you find it on the left hand side or the right hand side of an assignment:

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

The subscript operator, in combination with a custom implementation of getAt/putAt is a convenient way for destructuring objects:

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

8.5. Safe index operator

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

String[] array = ['a', 'b']
assert 'b' == array?[1]      // get using normal array index
array?[1] = 'c'              // set using normal array index
assert 'c' == array?[1]

array = null
assert null == array?[1]     // return null for all index values
array?[1] = 'c'              // quietly ignore attempt to set value
assert null == array?[1]

def personInfo = [name: 'Daniel.Sun', location: 'Shanghai']
assert 'Daniel.Sun' == personInfo?['name']      // get using normal map index
personInfo?['name'] = 'sunlan'                  // set using normal map index
assert 'sunlan' == personInfo?['name']

personInfo = null
assert null == personInfo?['name']              // return null for all map values
personInfo?['name'] = 'sunlan'                  // quietly ignore attempt to set value
assert null == personInfo?['name']

8.6. Membership operator

The membership operator (in) is equivalent to calling the isCase method. In the context of a List, it is equivalent to calling contains, like in the following example:

def list = ['Grace','Rob','Emmy']
assert ('Emmy' in list)                     (1)
1 equivalent to calling list.contains('Emmy') or list.isCase('Emmy')

8.7. Identity operator

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

def list1 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']        (1)
def list2 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']        (2)
assert list1 == list2                                       (3)
assert !list1.is(list2)                                     (4)
1 Create a list of strings
2 Create another list of strings containing the same elements
3 using ==, we test object equality
4 but using is, we can check that references are distinct

8.8. Coercion operator

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

Integer x = 123
String s = (String) x                                   (1)
1 Integer is not assignable to a String, so it will produce a ClassCastException at runtime

This can be fixed by using coercion instead:

Integer x = 123
String s = x as String                                  (1)
1 Integer is not assignable to a String, but use of as will coerce it to a String

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

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

8.9. Diamond operator

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

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

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

8.10. Call operator

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

class MyCallable {
    int call(int x) {           (1)
        2*x
    }
}

def mc = new MyCallable()
assert mc.call(2) == 4          (2)
assert mc(2) == 4               (3)
1 MyCallable defines a method named call. Note that it doesn’t need to implement java.util.concurrent.Callable
2 we can call the method using the classic method call syntax
3 or we can omit .call thanks to the call operator