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'
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
以下是运算符及其相应方法的完整列表: