Groovy 官方文档翻译 - 02 - 操作符

版权声明:本文由“勤劳的悄悄”翻译,并发布在“简书”网,转帖请保留该版权声明。

1.数学运算符

1.1.常用数学运算符

下面是常用的二元运算符:

Operator Purpose Remarks
+
-
*
/ 整除使用 intdiv() 方法
% 求余
** 乘方
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

自加自减也是一元运算符

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.关系运算符

运算符 用途
== 相等
!= 不等
< 小于
<= 小于等于
> 大于
>= 大于等于

例子:

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

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

assert 5 > 1
assert 5 >= -2

3.逻辑运算符

有三种逻辑运算符

  • &&:逻辑与
  • ||:逻辑或
  • !: 逻辑非

例子

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

3.1.优先级

逻辑非 > 逻辑与 > 逻辑或

assert (!false && false) == false 
assert true || true && false   

3.2.短路

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

有四种:

  • &: 与
  • |: 或
  • ^: 异或
  • ~: 非
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   

5.条件运算符

5.1.非运算

assert (!true)    == false                      
assert (!'foo')   == false                      
assert (!'')      == true           

5.2.三元表达式

下面的代码

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

可以写成

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

考虑到 Groovy 的真值表,可以进一步简化为

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

5.3.艾维斯运算符

代码进一步简化如下

displayName = user.name ? user.name : 'Anonymous'   
displayName = user.name ?: 'Anonymous'              

6.对象运算符

6.1.空对象安全引用运算符

引用空对象会抛出 NullPointerException 异常。安全引用运算符会避免抛出这个异常,仅仅返回 null 值

def person = Person.find { it.id == 123 }    
def name = person?.name                      
assert name == null   

6.2.直接字段访问操作符

通常情况下对字段访问都要经过 getXXX 方法

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'   

直接字段访问 .@ 可以跳过 getXXX 方法,直接获得字段的值

assert user.@name == 'Bob'     

6.3.方法指针操作符

方法指针 (.&) 操作符可以获取一个方法指针,后面再调用这个指针

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

方法指针获得的值是一个groovy.lang.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']    

Groovy 允许重载多个同名方法,如果方法指针指向重载同名方法,则根据参数类型来判断调用哪个方法

def doSomething(String str) { str.toUpperCase() }    
def doSomething(Integer x) { 2*x }                   
def reference = this.&doSomething                    
assert reference('foo') == 'FOO'                     
assert reference(123)   == 246   

7.正则表达式运算符

7.1.模式运算符

模式运算符 (~) 创建一个 java.util.regex.Pattern 对象

def p = ~/foo/
assert p instanceof Pattern

其实不一定非要用斜杠创建模式,其他类型的字符串也可以充当模式

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

7.2.匹配对象操作符

匹配对象操作符 =~ 直接创建一个 java.util.regex.Matcher 对象

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

!m 相当于调用 if (!m.find())

7.3.直接匹配操作符

直接匹配操作符 (==~) 直接返回一个 boolean

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

8.其他操作符

8.1.扩展操作符

将“对象列表”转换成“对象属性的列表”。有点像 Python 的列表生成

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

这种转换是空指针安全的,遇到空指针仅仅返回 null,而不会抛出 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       

扩展操作符的本质是迭代调用并生成列表,因此任何实现了 Iterable 接口的对象都可以使用扩展操作符

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']
8.1.1.解包方法参数

下面这个方法需要三个参数

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

下面这个列表有三个元素

def args = [4,5,6]

将列表解包为三个参数传给方法

assert function(*args) == 26

位置可以随意

args = [4]
assert function(*args,5,6) == 26
8.1.2.解包列表元素
def items = [4,5]                      
def list = [1,2,3,*items,6]            
assert list == [1,2,3,4,5,6]      
8.1.3.解包键值对
def m1 = [c:3, d:4]                   
def map = [a:1, b:2, *:m1]            
assert map == [a:1, b:2, c:3, d:4]    

如果出现重复的的键,后面的值将替换前面的值

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] 

8.2.范围操作符

范围操作符 (..) 创建一个 groovy.lang.Range 对象,这个类型同时实现了 List 接口

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              

Range 对象是轻量级的,仅仅存储了上下边界,因此要创建 Range 还必须实现 Comparable 接口中的 next() 方法和 previous() 方法。因为字符对象实现了这两个方法,因此可以向下面这样用

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

8.3.比较操作符

比较操作符 (<=>) 其实就是 compareTo 方法

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

8.4.索引操作符

索引操作符本质上是调用 getAtputAt 方法,可以切片获取或者切片赋值

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]  

可以重写 getAt/putAt 方法,实现一些自定义的行为

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'        

8.5.成员操作符

成员操作符 (in) 相当于调用了列表的 contains 方法或者 isCase 方法

def list = ['Grace','Rob','Emmy']
assert ('Emmy' in list)  

calling list.contains('Emmy')list.isCase('Emmy')是同样的功能

8.6.同一操作符

在 Groovy 中, == 仅仅检测值是否相同,如果想检测是否是同一个对象,使用 is 操作符

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)  

8.7.类型转换操作符

转型操作符 (as) 可以转换一些不兼容变量的类型,例如下面的转换会抛出异常

Integer x = 123
String s = (String) x  

用转型操作符可以正确转换

Integer x = 123
String s = x as 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)     

8.8.钻石操作符

钻石操作符 (<>) 仅仅是为了兼容 Java 7 中的泛型,在 Groovy 中没有什么用处

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

8.9.调用操作符

调用操作符 () 隐式调用 call 方法。可以重写 call 实现一些自定义的行为,然后用调用操作符调用

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

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

9.操作符优先级

操作符优先级表格

优先级 操作符 说明
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 index, post inc/decrement
2 ** power
3 ++ -- + - pre inc/decrement, unary plus, unary minus
4 * / % multiply, div, modulo
5 + - addition, subtraction
6 << >> >>> .. ..< left/right (unsigned) shift, inclusive/exclusive range
7 < <= > >= in instanceof as less/greater than/or equal, in, instanceof, type coercion
8 == != <=> equals, not equals, compare 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) {                     
        return new Bucket(this.size + other.size)
    }
}

方法 plus() 重载了 + 操作符

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

下表是各个操作符的函数名称

操作符 方法 操作符 方法
+ 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.bitwiseNegative()

推荐阅读更多精彩内容