Groovy 操作符

文章主要讲述Groovy操作符的使用

1.算术运算符

Groovy支持常用的算术运算符,就像Java一样。所有的Java算术运算符在这里都是支持的:

1.1. 普通算术运算符

下面的二进制算术运算符在Groovy中是有效的:

运算符用法备注

+

加法


-

减法


*

乘法


/

除法


%

取余


**

幂运算


示例:

assert 1 + 2 == 3 
assert 4 - 3 == 1 
assert 3 * 5 == 15 
assert 3 / 2 == 1.5 
assert 10 % 3 == 1 
assert 2 ** 3 == 8

1.2. 一元运算

在一元运算中,+和-都是有效的:

assert +3 == 3 
assert -4 == 0 - 4 
assert -(-1) == 1

In terms of unary arithmetics operators, the ++ (increment) and -- (decrement) operators are available, both in prefix and postfix notation:

def a = 2 
def b = a++ * 3  
assert a == 3 && b == 6 
def c = 3 
def d = c-- * 2  
assert c == 2 && d == 6 
def e = 1 
def f = ++e + 3  
assert e == 2 && f == 5 
def g = 4 
def h = --g + 1  
assert g == 3 && h == 4

1.3.赋值算术运算符

  • +=

  • -=

  • *=

  • /=

  • %=

  • **=

def a = 4
a += 3

assert a == 7

def b = 5
b -= 3

assert b == 2

def c = 5
c *= 3

assert c == 15

def d = 10
d /= 2

assert d == 5

def e = 10
e %= 3

assert e == 1

def f = 3
f **= 2

assert f == 9

2. 关系运算符

Relational operators allow comparisons between objects, to know if two objects are the same or different, or if one is greater than, less than, or equal to the other.

The following operators are available:

运算符用法

==

等于

!=

不等于

<

小于

<=

小于等于

>

大于

>=

大于等于

===

完全等于 (Groovy 3.0.0以上版本)

!==

不完全等于 (Groovy 3.0.0以上版本)

简单示例:

assert 1 + 2 == 3
assert 3 != 4

assert -2 < 3
assert 2 <= 2
assert 3 <= 4

assert 5 > 1
assert 5 >= -2

=== 和 !== are supported which are the same as calling the is() method, and negating a call to the is() method respectively.

import groovy.transform.EqualsAndHashCode

@EqualsAndHashCode
class Creature { String type }

def cat = new Creature(type: 'cat')
def copyCat = cat
def lion = new Creature(type: 'cat')

assert cat.equals(lion) // Java logical equality
assert cat == lion      // Groovy shorthand operator

assert cat.is(copyCat)  // Groovy identity
assert cat === copyCat  // operator shorthand
assert cat !== lion     // negated operator shorthand

3. 逻辑运算符

Groovy offers three logical operators for boolean expressions:

  • &&: logical "and"

  • ||: logical "or"

  • !: logical "not"

Let’s illustrate them with the following examples:

assert !false  assert true && true  assert true || false

"not" false is true

true "and" true is true

true "or" false is true

3.1. 优先运算

The logical "not" has a higher priority than the logical "and".

assert (!false && false) == false

断言的结果是true,因为“!”运算符,比 && 有更高的优先级。

&& 比 || 有更高的优先级

assert true || true && false

3.2. 短路运算符

逻辑非 || 运算 支持短路运算: 如果左边的值为真,无论什么时候都会返回真,所以不会计算右边的运算。只有在左边的值为假的时候,右边的操作数才会运算。

对于逻辑与 && 也是一样的,如果左边的操作数为假,无论什么时候都会返回假,所以不会计算右边的操作数,只有在左边的操作数为true的情况下,才会计算右边的操作数。

boolean checkIfCalled() {
    called = true
}

called = false
true || checkIfCalled()
assert !called

called = false
false || checkIfCalled()
assert called

called = false
false && checkIfCalled()
assert !called

called = false
true && checkIfCalled()
assert called

4. 按位和位移操作符

4.1. 按位操作符

Groovy 提供了四种按位操作符:

  • &: 按位“与”

  • |: 按位 "或"

  • ^: 按位 "异或" (排除 "or")

  • ~: 按位取反

按位操作符适用于 byte, short, int, long, 或是BigInteger类型的参数。如果其中一个参数是BigInteger,那么结果就是BigInteger;如果其中一个参数是long型,那么结果就是long,否则就是int类型的。

int a = 0b00101010
assert a == 42
int b = 0b00001000
assert b == 8
assert (a & a) == a
assert (a & b) == b
assert (a | a) == a
assert (a | b) == a

int mask = 0b11111111
assert ((a ^ a) & mask) == 0b00000000
assert ((a ^ b) & mask) == 0b00100010
assert ((~a) & mask)    == 0b11010101

值得注意的是基础类型的内部表示遵循了Java Language Specification。

在Groovy中,按位运算符是可重载的,意味着你可以定义任意类型对象的这些操作符行为。

4.2. 位移运算符

Groovy提供三种位移运算符:

  • <<: 向左位移

  • >>: 向右位移

  • >>>: right shift unsigned 右移无符号

All three operators are applicable where the left argument is of type byte, short, int, or long. The first two operators can also be applied where the left argument is of type BigInteger. If the left argument is a BigInteger, the result will be of type BigInteger; otherwise, if the left argument is a long, the result will be of type long; otherwise, the result will be of type int:

assert 12.equals(3 << 2)  
assert 24L.equals(3L << 3)  
assert 48G.equals(3G << 4)  
assert 4095 == -200 >>> 20 
assert -1 == -200 >> 20 
assert 2G == 5G >> 1 
assert -3G == -5G >> 1

在Groovy中,位移运算符是可重载的,意味着你可以自定义任意对象的操作的行为。

5. 条件运算符

5.1. 非运算符

assert (!true) == false    //true的非运算是false
assert (!'foo') == false   // 非空的非运算是false
assert (!'') == true       // 空值的非运算是true

5.2. 三元运算符

三元运算符是if/else判断并赋值的一种简写方式。

比如下面的判断代码:

if (string!=null && string.length()>0) {
     result = 'Found' 
} else {
     result = 'Not found' 
}

可以写成:

result = (string!=null && string.length()>0) ? 'Found' : 'Not found'

三元运算兼容Groovy truth,所以你可以写得更简单一些:

result = string ? 'Found' : 'Not found'

5.3. Elvis 操作符

“Elvis 操作符”是三元操作符的一种缩写。

displayName = user.name ? user.name : 'Anonymous'  // 这种写法,你不得不重复的赋值
displayName = user.name ?: 'Anonymous'             // 使用Elvis 操作符,你可以少写很多重复的代码

Elvis 操作符减少了很多冗余的代码,减少了出错的风险。

5.4. Elvis 赋值操作符

Groovy 3.0.0 引入了Elvis 操作符,例如:

import groovy.transform.ToString

@ToString
class Element {
    String name
    int atomicNumber
}

def he = new Element(name: 'Helium')
he.with {
    name = name ?: 'Hydrogen'   // existing Elvis operator
    atomicNumber ?= 2           // new Elvis assignment shorthand
}
assert he.toString() == 'Element(Helium, 2)'

6. 对象操作符

6.1. Safe navigation 操作符

Safe navigation 操作符就是为了防止出现空指针异常,以往在使用一个对象的时候,都需要检查空指针。使用Safe navigation操作符,可以省去对象检查,直接返回一个null。

def person = Person.find { it.id == 123 }   // find 会返回一个空实例
def name = person?.name                     // 使用空安全操作符防止了空指针异常  
assert name == null                         // 结果是null

6.2. 属性直接访问操作符

class User {
    public final String name                 // 公共属性
    User(String name) { this.name = name}
    String getName() { "Name: $name" }       // name属性的getter方法,返回一个自定义字符串
}
def user = new User('Bob')
assert user.name == 'Name: Bob'              // 访问name属性会直接调用getter方法

如果你想不触发getter方法,可以在调用属性时前面加上一个@符号,这样就可以直接访问属性的原始值。

assert user.@name == 'Bob'

6.3. 方法指针操作符

方法指针操作符 (.&) 可以用来把一个方法的引用存到一个变量中,稍后进行调用。

def str = 'example of method reference'   // 字符串变量
def fun = str.&toUpperCase                // 将toUpperCase方法存到fun变量中                 
def upper = fun()                         // 使用变量fun可以像使用普通方法一样使用
assert upper == str.toUpperCase()         // 结果是true

方法指针还有很多更高级的使用方法,首先一个方法指针就量个groovy.lang.Closure,所以你可以将closure放在你需要的地方:

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']

方法指针受接收者和方法名称的约束,参数在运行时才会入栈,也就是说多个同名方法,语法是一样的,只有在运行调用的时候才会进行区分。

def doSomething(String str) { str.toUpperCase() }  // 定义一个接收String参数的方法   
def doSomething(Integer x) { 2*x }                 // 定义一个接收Integer参数的方法
def reference = this.&doSomething                  // 创建一个doSomething的方法指针
assert reference('foo') == 'FOO'                   // 调用String参数方法
assert reference(123)   == 246                     // 调用Integer参数方法

为了对齐Java8的方法引用,Groovy3或更高版本,可以将方法名称的指针指向构造函数:

def foo  = BigInteger.&new 
def fortyTwo = foo('42') 
assert fortyTwo == 42G

在Groovy3或更高的版本中,你将方法指针指向类的方法实例,这个方法指针会接收一个调用实例作为参数:

def instanceMethod = String.&toUpperCase 
assert instanceMethod('foo') == 'FOO'

For backwards compatibility, any static methods that happen to have the correct parameters for the call will be given precedence over instance methods for this case.

6.4. 方法引用运算符

The Parrot parser in Groovy 3+ supports the Java 8+ method reference operator. The method reference operator (::) can be used to reference a method or constructor in contexts expecting a functional interface. This overlaps somewhat with the functionality provided by Groovy’s method pointer operator. Indeed, for dynamic Groovy, the method reference operator is just an alias for the method pointer operator. For static Groovy, the operator results in bytecode similar to the bytecode that Java would produce for the same context.

Some examples highlighting various supported method reference cases are shown in the following script:

import groovy.transform.CompileStatic
import static java.util.stream.Collectors.toList

@CompileStatic
void methodRefs() {
    assert 6G == [1G, 2G, 3G].stream().reduce(0G, BigInteger::add)

    assert [4G, 5G, 6G] == [1G, 2G, 3G].stream().map(3G::add).collect(toList())

    assert [1G, 2G, 3G] == [1L, 2L, 3L].stream().map(BigInteger::valueOf).collect(toList())

    assert [1G, 2G, 3G] == [1L, 2L, 3L].stream().map(3G::valueOf).collect(toList())
}

methodRefs()

class instance method reference: add(BigInteger val) is an instance method in BigInteger

object instance method reference: add(BigInteger val) is an instance method for object 3G

class static method reference: valueOf(long val) is a static method for class BigInteger

object static method reference: valueOf(long val) is a static method for object 3G (some consider this bad style in normal circumstances)

Some examples highlighting various supported constructor reference cases are shown in the following script:

@CompileStatic
void constructorRefs() {
    assert [1, 2, 3] == ['1', '2', '3'].stream().map(Integer::new).collect(toList())

    def result = [1, 2, 3].stream().toArray(Integer[]::new)
    assert result instanceof Integer[]
    assert result.toString() == '[1, 2, 3]'
}

constructorRefs()



class constructor reference

array constructor reference


7. 正则运算符

7.1. Pattern 运算符

The pattern operator (~) provides a simple way to create a java.util.regex.Pattern instance:

def p = ~/foo/ assert p instanceof Pattern

while in general, you find the pattern operator with an expression in a slashy-string, it can be used with any kind of String in Groovy:

p = ~'foo'  p = ~"foo"  p = ~$/dollar/slashy $ string/$                                    p = ~"${pattern}"

using single quote strings

using double quotes strings

the dollar-slashy string lets you use slashes and the dollar sign without having to escape them

you can also use a GString!



While you can use most String forms with the Pattern, Find and Match operators, we recommend using the slashy string most of the time to save having to remember the otherwise needed escaping requirements.


7.2. Find 运算符

Alternatively to building a pattern, you can use the find operator =~ to directly create a java.util.regex.Matcher instance:

def text = "some text to match" def m = text =~ /match/  assert m instanceof Matcher  if (!m) {  throw new RuntimeException("Oops, text not found!") }

=~ creates a matcher against the text variable, using the pattern on the right hand side

the return type of =~ is a Matcher

equivalent to calling if (!m.find(0))

Since a Matcher coerces to a boolean by calling its find method, the =~ operator is consistent with the simple use of Perl’s =~ operator, when it appears as a predicate (in if, ?:, etc.). When the intent is to iterate over matches of the specified pattern (in while, etc.) call find() directly on the matcher or use the iterator DGM.

7.3. Match 运算符

The match operator (==~) is a slight variation of the find operator, that does not return a Matcher but a boolean and requires a strict match of the input string:

m = text ==~ /match/  assert m instanceof Boolean  if (m) {  throw new RuntimeException("Should not reach that point!") }



==~ matches the subject with the regular expression, but match must be strict

the return type of ==~ is therefore a boolean

equivalent to calling if (text ==~ /match/)


7.4. 比较 Find vs Match 运算符

Typically, the match operator is used when the pattern involves a single exact match, otherwise the find operator might be more useful.

assert 'two words' ==~ /\S+\s+\S+/
assert 'two words' ==~ /^\S+\s+\S+$/
assert !(' leading space' ==~ /\S+\s+\S+/)

def m1 = 'two words' =~ /^\S+\s+\S+$/
assert m1.size() == 1
def m2 = 'now three words' =~ /^\S+\s+\S+$/
assert m2.size() == 0
def m3 = 'now three words' =~ /\S+\s+\S+/
assert m3.size() == 1
assert m3[0] == 'now three'
def m4 = ' leading space' =~ /\S+\s+\S+/
assert m4.size() == 1
assert m4[0] == 'leading space'
def m5 = 'and with four words' =~ /\S+\s+\S+/
assert m5.size() == 2
assert m5[0] == 'and with'
assert m5[1] == 'four words'



equivalent, but explicit ^ and $ are discouraged since they aren’t needed

no match because of leading space

one match

^ and $ indicate exact match required

zero matches

one match, greedily starting at first word

one match, ignores leading space

two matches


8. 其他运算符

8.1. 延展操作符

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')]
def makes = cars*.make
assert makes == ['Peugeot', 'Renault']

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

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,
        new Car(make: 'Renault', model: 'Clio')]
assert cars*.make == ['Peugeot', null, 'Renault']
assert null*.make == null

build a list for which one of the elements is null

using the spread operator will not throw a NullPointerException

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. 传递方法参数

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. 扩展 list 元素

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]  
def list = [1,2,3,*items,6]  
assert list == [1,2,3,4,5,6]



items is a list

we want to insert the contents of the items list directly into list without having to call addAll

the contents of items has been inlined into list


8.1.3. 扩展 map 元素

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]  
def map = [a:1, b:2, *:m1]  
assert map == [a:1, b:2, c:3, d:4]

m1 is the map that we want to inline

we use the *:m1 notation to spread the contents of m1 into map

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]  def map = [a:1, b:2, *:m1, d: 8]  assert map == [a:1, b:2, c:3, d:8]



m1 is the map that we want to inline

we use the *:m1 notation to spread the contents of m1 into map, but redefine the key d after spreading

map contains all the expected keys, but d was redefined


8.2. 范围运算符

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

def range = 0..5
assert (0..5).collect() == [0, 1, 2, 3, 4, 5]
assert (0..<5).collect() == [0, 1, 2, 3, 4]
assert (0..5) instanceof List
assert (0..5).size() == 6

a simple range of integers, stored into a local variable

an IntRange, with inclusive bounds

an IntRange, with exclusive upper bound

a groovy.lang.Range implements the List interface

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 运算符

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. 下标运算符

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  
list[2] = 4  
assert list[0..2] == [0,1,4]  
list[0..2] = [6,6,6]  
assert list == [6,6,6,3,4]

[2] can be used instead of getAt(2)

if on left hand side of an assignment, will call putAt

getAt also supports ranges

so does putAt

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) {
        switch (i) {
            case 0: return id
            case 1: return name
        }
        throw new IllegalArgumentException("No such element $i")
    }
    void putAt(int i, def value) {
        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')
assert user[0] == 1
assert user[1] == 'Alex'
user[1] = 'Bob'
assert user.name == 'Bob'



the User class defines a custom getAt implementation

the User class defines a custom putAt implementation

create a sample user

using the subscript operator with index 0 allows retrieving the user id

using the subscript operator with index 1 allows retrieving the user name

we can use the subscript operator to write to a property thanks to the delegation to putAt

and check that it’s really the property name which was changed


8.5. 安全索引运算符

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. 成员操作符

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)



equivalent to calling list.contains('Emmy') or list.isCase('Emmy')


8.7. 恒等运算符

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']
def list2 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']
assert list1 == list2
assert !list1.is(list2)



Create a list of strings

Create another list of strings containing the same elements

using ==, we test object equality

but using is, we can check that references are distinct


8.8. 强制运算符

强制操作符(as)是casting的一个变体。强制将对象从一种类型转换为另一种类型,而不兼容赋值。举个例子:

Integer x = 123 
String s = (String) x

Integer is not assignable to a String, so it will produce a ClassCastException at runtime

这可以通过使用强制来解决:

Integer x = 123 
String s = x as String

Integer is not assignable to a String, but use of as will coerce it to a String

当对象被强制到另一个对象时,除非目标类型与源类型相同,否则强制将返回新对象。强制规则根据源和目标类型不同而不同,如果未找到转换规则,强制可能会失败。自定义转换规则可以通过asType方法实现:

class Identifiable {
    String name
}
class User {
    Long id
    String name
    def asType(Class target) {
        if (target == Identifiable) {
            return new Identifiable(name: name)
        }
        throw new ClassCastException("User cannot be coerced into $target")
    }
}
def u = new User(name: 'Xavier')
def p = u as Identifiable
assert p instanceof Identifiable
assert !(p instanceof User)



the User class defines a custom conversion rule from User to Identifiable

we create an instance of User

we coerce the User instance into an Identifiable

the target is an instance of Identifiable

the target is not an instance of User anymore


8.9. 菱形操作符

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. 操作符调用

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) {
        2*x
    }
}

def mc = new MyCallable()
assert mc.call(2) == 4
assert mc(2) == 4

MyCallable defines a method named call. Note that it doesn’t need to implement java.util.concurrent.Callable

we can call the method using the classic method call syntax

or we can omit .call thanks to the call operator

9. 运算符优先级

下面的表格是运算符的优先顺序:

LevelOperator(s)Name(s)

1

new   ()

object creation, explicit parentheses


()   {}   []

method call, closure, literal list/map


.   .&   .@

member access, method closure, field/attribute access


?.   *   *.   *:

safe dereferencing, spread, spread-dot, spread-map


~   !   (type)

bitwise negate/pattern, not, typecast


[]   ?[]   ++   --

list/map/array (safe) index, post inc/decrement

2

**

power

3

++   --   +   -

pre inc/decrement, unary plus, unary minus

4

*   /   %

multiply, div, remainder

5

+   -

addition, subtraction

6

<<   >>   >>>   ..   ..<

left/right (unsigned) shift, inclusive/exclusive range

7

<   <=   >   >=   in   !in   instanceof   !instanceof   as

less/greater than/or equal, in, not in, instanceof, not instanceof, type coercion

8

==   !=   <=>   ===   !==

equals, not equals, compare to, identical to, not identical to


=~   ==~

regex find, regex match

9

&

binary/bitwise and

10

^

binary/bitwise xor

11

|

binary/bitwise or

12

&&

logical and

13

||

logical or

14

? :

ternary conditional


?:

elvis operator

15

=   **=   *=   /=   %=   +=   -=  
<<=   >>=   >>>=   &=   ^=   |=     ?=

various assignments


10. 运算符重载

Groovy 允许重载多个运算符,可以在类中重载不同的运算符方法,下面是一个简单的例子:

class Bucket {
    int size

    Bucket(int size) { this.size = size }

    Bucket plus(Bucket other) {                     // Bucket实现了一个特殊的方法plus()
        return new Bucket(this.size + other.size)
    }
}

只是实现了plus方法,Bucket类就可以使用 + 运算符:

def b1 = new Bucket(4) 
def b2 = new Bucket(11) 
assert (b1 + b2).size == 15

所有(非比较)的Groovy运算符,都有相对应的类实现方法,唯一要求的就是方法是公有的,正确的名字,正确的参数。参数类型对应着不同运算符支持的类型:

assert (b1 + 11).size == 15
Bucket plus(int capacity) {
 return new Bucket(this.size + capacity) 
}

Here is a complete list of the operators and their corresponding methods:

OperatorMethodOperatorMethod

+

a.plus(b)

a[b]

a.getAt(b)

-

a.minus(b)

a[b] = c

a.putAt(b, c)

*

a.multiply(b)

a in b

b.isCase(a)

/

a.div(b)

<<

a.leftShift(b)

%

a.mod(b)

>>

a.rightShift(b)

**

a.power(b)

>>>

a.rightShiftUnsigned(b)

|

a.or(b)

++

a.next()

&

a.and(b)

--

a.previous()

^

a.xor(b)

+a

a.positive()

as

a.asType(b)

-a

a.negative()

a()

a.call()

~a

a.bitwiseNegate()