Skip to content

Operators

Philip Ford edited this page Sep 16, 2018 · 20 revisions

Contents

Overview

Groovy uses the same operators as in Java, but adds some others.

Direct field access operator: .@

The use of .@ forces usage of the field instead of the getter for that field.

Example:

class User {
    public final String name                 
    User(String name) { this.name = name}
    String getName() { "Name: $name" }       
}
def user = new User('Bob')
assert user.name == 'Name: Bob' 

Here the user.name call triggers a call to the getter for name. If you want to retrieve the field instead of calling the getter, you can use the direct field access operator:

assert user.@name == 'Bob'  

Elvis Operator: ?:

A more concise ternary operator which says, "if the expression is null, do this."

def sampleText
 
// Normal ternary operator.
def ternaryOutput = (sampleText != null) ? sampleText : 'Hello Groovy!'
 
// The Elvis operator in action. We must read: 'If sampleText is not null assign
// sampleText to elvisOuput, otherwise assign 'Viva Las Vegas!' to elvisOutput.
def elvisOutput = sampleText ?: 'Viva Las Vegas!'

Method pointer operator: .&

The method pointer operator (.&) call be used to store a reference to a method in a variable, in order to call it later:

def str = 'example of method reference'            
def fun = str.&toUpperCase                         
def upper = fun()                                  
assert upper == str.toUpperCase()

In this example:

  • the str variable contains a String
  • we store a reference to the toUpperCase method on the str instance inside a variable named fun
  • fun can be called like a regular method

There are multiple advantages in using method pointers. First of all, the type of such a method pointer is a groovy.lang.Closure, so it can be used in any place a closure would be used. In particular, it is suitable to convert an existing method for the needs of the strategy pattern:

Use of methods points for the Strategy pattern

def transform(List elements, Closure action) {                    
    def result = []
    elements.each {
        result << action(it)
    }
    result
}
String describe(Person p) {                                       
    "$p.name is $p.age"
}
def action = this.&describe                                       
def list = [
    new Person(name: 'Bob',   age: 42),
    new Person(name: 'Julia', age: 35)]                           
assert transform(list, action) == ['Bob is 42', 'Julia is 35']   

In this example:

  • the transform method takes each element of the list and calls the action closure on them, returning a new list
  • we define a function that takes a Person and returns a String
  • we create a method pointer on that function
  • we create the list of elements we want to collect the descriptors
  • the method pointer can be used where a Closure was expected

Null-Check Operator: ?.

TBD

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

Spread Operator: *.

The Spread Operator (*.) is used to invoke an action on all items of an aggregate object. It is equivalent to calling collect() on a Collection, 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')]       
def makes = cars*.make                                
assert makes == ['Peugeot', 'Renault'] 

In this example, we:

  • build a list of Car items. The list is an aggregate of objects.
  • call the spread operator on the list, accessing the make property of each item
  • return a list of strings containing the values of make for each Car.

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

Spreading method arguments

You can concert a List to the arguments of a method call:

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


// you can call the method without having to define intermediate variables
def args = [4,5,6]
assert function(*args) == 26

//It is even possible to mix normal arguments with spread ones:
args = [4]
assert function(*args,5,6) == 26

Spreading list items

Spreading map items

Operator Overloading

While Groovy can use the same operators as Java, operators are all implemented by methods in Groovy. This means we can do operator overriding in our own classes. This is very useful and can make more concise code.

Operators and their corresponding methods

Operator Method
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)
a >>> b a.rightShiftUnsigned(b)
switch(a) { case(b) : } b.isCase(a)
~a a.negate()
-a a.negative()
+a a.positive()
a == b a.equals(b)
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

Example: Overloading +

class Money {
   def amount

   Money plus(Money other) {
       new Money(amount: this.amount + other.amount)
   }

   boolean equals(Object other) {
       amount == other.amount
   }

   int hashCode() {
       amount.hashCode()
   }

   String toString() {
       amount
   }
}

def m1 = new Money(amount: 100)
def m2 = new Money(amount: 1)

assert (m1 + m2).amount == 101  // plus()
assert m1 + m2 == new Money(amount: 101)  // equals() and plus()

References

Clone this wiki locally