Groovy 运算符

Groovy 运算符

官方文档(运算符):http://www.groovy-lang.org/operators.html

1. 算术运算符

1.1 除法

如果操作数是float或double,则除法//=会产生一个double,反之,则会产生一个BigDecimal的结果(当这两个操作数都是整型short, char, byte, int, long, BigInteger 或 BigDecimal的组合)。

对于像Java那样的整数除法,应该使用intdiv()方法,Groovy并没有提供一个专用的整数除法运算符。

1.2 次方运算符

**

assert  2 ** 3 == 8

def f = 3
f **= 2
assert f == 9

2. 条件运算符

2.1 三元运算符

Java写法:

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

Groovy的更简短:

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

Groovy truth

2.2 Elvis operator

Elvis运算符 ?:是三元运算符的缩短:

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

使用Elvis操作符可以减少代码的冗余,减少重构时出错的风险,消除条件和正返回值的复制。

3. 对象操作符

3.1 安全导航操作符

安全导航操作符?.用于避免NullPointerException,通常使用对象引用时,在访问对象的方法或属性之前,可能需要验证引用是否为null,为了避免这种情况,安全导航操作符将简单地返回null而不是抛出异常(如果为引用为null,则返回null)。

class Person{
    public String name = 'aaa'
}

Person person              
println(person?.name)  // null
println(person.name)  // java.lang.NullPointerException

3.2 直接字段访问操作符

因为Groovy自动支持属性的getter方法,但有时我们会自定义getter方法,如果不想调用自定义的getter方法,那么可以用直接字段访问操作符 .@

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'  // 会触发getter方法调用
assert user.@name == "Bob"  // 直接访问name字段

.@会强制使用字段而不是getter

3.3 方法指针操作符

方法指针操作符 .& 将对方法的引用存储于变量中,以便稍后调用它:

def str = 'example String'
def upperFun = str.&toUpperCase //将str实例的toUpperCase方法的引用存储到upperFun变量内
def result = upperFun()  // 像常规方法一样调用upperFun
assert result == str.toUpperCase()  // 与在str上直接调用方法的结果一样
println(result)  // EXAMPLE STRING

使用方法指针有多个优点。首先,方法指针的类型是 groovy.lang.Closure,所以可以在任何使用闭包的地方使用它。特别适合于针对策略模式的需求转换现有方法的情景:

class Person{
    String name
    int age
    Person(String name, int age){
        this.name = name
        this.age = age
    }
}
def list = [
    new Person("wzx", 24),
    new Person("lc", 22)]
    
//描述一个Person对象的方法
def describe(Person p){
    "$p.name is $p.age"
}   
  
//转换方法:遍历List,将每一个元素都执行一次describe方法
def transform(List elements, Closure closure){
    def list = [];
    elements.each{
        list << closure(it)
    }
    list
}

//存储describe方法引用的变量
def describeReference = this.&describe
assert transform(list, describeReference) == ['wzx is 24', 'lc is 22']

存储引用的变量只与方法名称绑定,其参数在运行时才会解析。这意味着如果有多个名称相同的方法,在运行时才会根据传入的参数调用适当的方法:

def doSomething(String str){str.toUpperCase()}
def doSomething(int num){num ** 2}
def reference = this.&doSomething
assert reference('abc') == 'ABC'
assert reference(3) == 9

4. 正则表达式操作符

4.1 Pattern操作符

Pattern操作符 ~ 提供了一个创建java.util.regex.Pattern实例的简单方式,通常和斜线字符串一起使用,不过可以与任何Groovy String一起使用。

def p = ~/foo/ 
p = ~'foo'                                                        
p = ~"foo"                                                        
p = ~$/dollar/slashy $ string/$                                   
p = ~"${pattern}"  
assert p instanceof Pattern 

创建Pattern后不能再改变

def pattern = 'aabb'
def p = ~"${pattern}"
assert p.toString() == 'aabb'

pattern = 'ttyy'
assert p.toString() == 'aabb'

4.2 查找操作符

可以使用 =~ 操作符直接构建一个java.util.regex.Matcher实例

def text = "some text to match"
def m = text =~ /.*match.*/                                           
assert m instanceof Matcher  // =~ 的返回类型为Matcher
assert m  // true,相当于调用了m.find()
println(m.group())  // some text to match

4.3 匹配操作符

==~ 返回的是Boolean

def text = 'text to match'
def m = text ==~ /.*match.*/ 
assert m instanceof Boolean  
if (m) {
   println('text match!')  // text match!
}

5. 其他操作符

5.1 扩展操作符

*. 用于对聚合对象的所有item进行操作,相当于在每一个item上操作,并将结果放在一个List中

class Person{
    String name
    int age
}
def persons = [
    new Person(name:'wzx', age:24),
    new Person(name:'lc', age:22)
]
def result = persons*.name;  //相当于在每一个item上操作,并将结果放在一个List中
assert result instanceof List
println(result)  // [wzx, lc]

扩展运算符是null安全的,如果集合的一个元素为null,它将返回null而不是抛出NullPointerException

def persons = [
    new Person(name:'wzx', age:24),
    null,
    new Person(name:'lc', age:22)
]
def result = persons*.name;
println(result)  // [wzx, null, lc]

对于实现了Iterable接口的任意类,都可以使用该操作符

5.1.1 扩展方法参数

* 例如,假设具有以下方法签名:

def fun(String a, String b, int c){
	"$a $b $c"
}

有以下列表:

def args = ["aaa", "bbb", 123]

可以调用该方法而不必定义中间变量:

assert function(*args) == 'aaa bbb 123'

甚至可以将普通参数与传播参数进行混合:

params = ["aaa", "bbb"]
assert function(*args, 123) == 'aaa bbb 123'

5.1.2 扩展List元素

*

def items = [4,5]                      
def list = [1,2,3,*items,6]  //不用addAll()而直接将items插入到list中    
assert list == [1,2,3,4,5,6] //items已经被内联到list中

5.1.3 扩展Map元素

*:

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]  // 扩展后d的值被重新指定了
assert map == [a:1, b:2, c:3, d:8]

5.2 范围操作符

Groovy提供..来创建范围对象

def range = 0..5 //用一个变量来存储一个整数范围对象                              
assert (0..5).collect() == [0, 1, 2, 3, 4, 5] // 一个IntRange,包含边界
assert (0..<5).collect() == [0, 1, 2, 3, 4] // 一个IntRange,不包含上边界
assert (0..5) instanceof List // groovy.lang.Range实际是一个List 
assert (0..5).size() == 6 //可以调用size()
assert ('a'..'d').collect() == ['a','b','c','d']

5.3 比较运算符

<=>代理了compareTo方法:

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

5.4 下标运算符

下标运算符[]是getAt或putAt的简写:

def list = [0,1,2,3,4]                    
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 Person{
    int id;
    String name;
    
    def getAt(int i){  //自定义的getAt实现
        switch(i){
            case 0: return id;
            case 1: return name;
        }
        throw new IllegalArgumentException("No such element $i")
    }
    void putAt(int i, def value){  //自定义的putAt实现
        switch(i){
            case 0: this.id = value; return;
            case 1: this.name = value; return;
        }
        throw new IllegalArgumentException("No such element $i")
    }
}

def p = new Person(id:10001, name:"wzx")
assert p[0] == 10001;
assert p[1] == "wzx"
p[1] = "lc"
assert p[1] == "lc"
assert p.name == "lc"

5.5 从属运算符

in相当于调用 isCase

def list = ['wzx', 'lc', 'aaa']
assert ('lc' in list)
assert list.contains('lc')
assert list.isCase('lc')

def map = ['wzx':24, 'lc':22]
assert ('wzx' in map)
assert map.containsKey('wzx')
assert map.isCase('wzx')

5.6 相等运算符

在Groovy中,使用==测试相等性不同于在Java中使用相同的运算符。在Groovy,它会调用equals。如果要比较引用的相等性,应该使用is

def list1 = ['wzx', 'lc']
def list2 = ['wzx', 'lc']
assert list1 == list2
assert !list1.is(list2)

5.7 强制转换操作符

类型不兼容:

String s = "123"
Integer x = (Integer) s

运行时抛出异常:GroovyCastException: Cannot cast object '123' with class 'java.lang.String' to class 'java.lang.Integer'

可以使用as完成转换:

String s = "123"
Integer x = s as Integer
println(x)  // 123

当一个对象被强转到另一个对象时,若目标类型与源类型相同,强转将返回一个新对象。转换的规则取决于源类型和目标类型,如果没有找到转换规则,强转可能会失败。通过asType方法可以自定义转换规则:

class Orange{
    String name
}
class Apple{
    String name
    def asType(Class target){
        if(target == Orange){
            return new Orange(name:name)
        }
        throw new ClassCastException("Apple cannot be coerced into $target")
    }
}

def apple = new Apple(name: "Apple")
def orange = apple as Orange  //强转成功则返回一个新对象
assert !(orange instanceof Apple)
assert orange instanceof Orange
println(orange.name); //Apple

5.8 调用操作符

操作符()是call的隐式调用,对于定义了call方法的任何对象都可以省略.call部分

class MyCallable {
    int call(int x) {           
        2*x
    }
}
def mc = new MyCallable()
assert mc.call(2) == 4          
assert mc(2) == 4 

6. 运算符优先级

7. 运算符重载

Groovy允许重载各种运算符,以便它们可以与自己的类一起使用,下面是个简单的类:

class Bucket {
    int size

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

    Bucket plus(Bucket other) {                     
        return new Bucket(this.size + other.size)
    }
}

Bucket 实现了一个特殊的方法 plus(),只要实现该方法,Bucket类就可以与+运算符一起使用:

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

所有Groovy运算符(非比较)都有一个相应的方法,可以在自己的类中实现。唯一的要求是方法是public,有正确的名称,正确的参数数量,参数类型取决于要运算符右侧要支持的类型,比如:

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

以下是运算符及其相应方法的完整列表:

posted @ 2017-08-18 19:36  zxlc  阅读(1487)  评论(0编辑  收藏  举报