Groovy运算符
Groovy运算符阅读笔记,非全文翻译。
1.算术运算符
Groovy支持常见的算术运算符,和所有Java支持的运算符。
1.1.普通算术运算符
Groovy支持的二元运算符:
运算符 | 用途 | 备注 |
---|---|---|
+ |
加 | |
- |
||
* |
乘 | |
/ |
除 | 整数除法用的是intdiv() ,更多的除法返回值类型参看integer division |
% |
取余 | |
** |
乘方 | 参看the power operation了解更多关于乘方返回值类型 |
1.2.一元运算符
+
和-
都可以作为一元运算符。并支持++
和--
方式的前缀和后缀
1.3.赋值运算符
上面的二元运算符也可以作为赋值运算符使用:
+=
-=
*=
/=
%=
**=
2.关系运算符
操作符 | 用途 |
---|---|
== |
相等 |
!= |
不相等 |
< |
小于 |
<= |
小于等于 |
> |
大于 |
>= |
大于等于 |
=== |
完全相同 |
!== |
不完全相同 |
===
和!==
的效果等同于调用is()
方法。
在Groovy中 ==
操作符是调用了对象的equals()
方法做比较,===
操作符是比较对象的内存地址是否相同
而在Java中,==
是比较对象的地址,如果需要比较对象的值是否相同则是调用对象的equals()
方法做比较。并且该对象需要重写equals()
和hashCode()
方法。
3.逻辑运算符
逻辑运算符有&&
,||
,!
3.1.优先级
!
> &&
> ||
assert (!false && false) == false // ! > &&, 结果为false
assert true || true && false // && > ||, 结果为true
3.2.短路
对于||
, 当运算符左边的值为true时,不会计算右边的值。只有左边的值为false时才会计算有百年的值。
对于&&
, 只有当左边的值为ture时才会计算右边的值。
4.位运算符
Groovy提供了四种位运算符
&
: 按位和|
: 按位或^
: 按位异或~
: 按位取反
Groovy的基础类型遵循Java的规范,基础类型都是有符号数,所以在使用按位取反的时候,使用mask来对指定的位置取反。
5.条件运算符
5.1.否操作符
使用!
反转结果
5.2.三元运算符
result = string ? 'Found' : 'Not found'
string?
判断字符串是否为空
5.3.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.空安全操作符
空安全操作符可以规避NullPointerException
.
def person = Person.find { it.id == 123 }
def name = person?.name
assert name == null
6.2.直接访问操作符
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'
对于上面的例子,在访问name
属性时实际是调用了对应的get方法。如果想要直接获取字段而不是调用方法,可以使用.@
操作符。
通过.@
可以直接访问字段而不是通过get方法
6.3.方法指针操作符
def str = 'example of method reference'
def fun = str.&toUpperCase // 将str的toUpperCase方法整体作为一个变量保存在fun中
def upper = fun() // 像调用常规方法一样调用fun方法
assert upper == str.toUpperCase() // fun方法的结果和str.toUpperCase()结果相同
方法指针是groovy.lang.Closure
类型的,因此可以在任何使用闭包的位置使用。
def transform(List elements, Closure action) {
def result = []
elements.each {
result << action(it)
}
result
}
//定义describe方法,作用是输出Person的信息
String describe(Person p) {
"$p.name is $p.age"
}
//方法指针保存在action中
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
在Groovy3.0及以上,可以使用new
作为一个方法名来指向一个构造函数。
例如:
def foo = BigInteger.&new
def fortyTwo = foo('42')
assert fortyTwo == 42G
在Groovy3.0及以上,可以使用方法指针指向一个类的实例方法,这个方法指针接收一个接收者类型的实例作为参数。例如:
def instanceMethod = String.&toUpperCase // 指向实例方法的方法指针
assert instanceMethod('foo') == 'FOO' // 方法指针接收一个实例
总结一下:
方法指针可以指向一个具体实例的方法,例如
Groovydef str = 'example of method reference' def fun = str.&toUpperCase
方法指针可以指向当前脚本的方法,例如:
GroovyString describe(Person p) { "$p.name is $p.age" } def action = this.&describe
方法指针可以指向多个重载方法,在运行时根据传参选择正确的方法执行
方法指针可以使用
new
来指向一个构造方法方法指针可以指向一个实例方法。
6.4.方法引用运算符
在Groovy 3.0及以上支持Java8以上的::
运算符。
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()
@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()
7.正则表达式运算符
7.1.Pattern运算符
Groovy中使用~
简化了创建java.util.regex.Pattern
实例。
def p = ~/foo/
assert p instanceof Pattern
~
支持Groovy中的任意String类型:
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 // 返回的类型是Matcher
if (!m) { // 等效于if (!m.find(0))
throw new RuntimeException("Oops, text not found!")
}
7.3.匹配运算符
==~
是Match.find的简单实用方式,它只返回布尔值。==~
是严格匹配的。
m = text ==~ /match/
assert m instanceof Boolean
if (m) {
throw new RuntimeException("Should not reach that point!")
}
8.其他运算符
8.1.扩展运算符
扩展点运算符*.
通常作用于集合对象。它会对集合的每一项都做同一运算并将结果保存在一个集合中。
class Car {
String make
String model
}
def cars = [
new Car(make: 'Peugeot', model: '508'),
new Car(make: 'Renault', model: 'Clio')]
def makes = cars*.make //访问cars中的每一个元素的make
assert makes == ['Peugeot', 'Renault'] //将cars中的每个元素的make字段值添加到列表中。
这里的cars*.make
等同于cars.collect{ it.make }
。当要访问聊表中元素的属性时,Groovy的GPath表示法允许使用这样的快捷方式来访问,它会自动扩展到每一个元素。
在上面的例子中使用cars.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']
当数据包含了多层结构时,使用多次*.
调用来访问数据。
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)
也可以使用collectNested
方法来获取所有元素的属性。
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.扩展方法参数
当方法传参于列表的各项一一对应时,可以使用扩展方法参数的方式调用方法,而不需要从列表中逐个取出参数传入方法。
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.扩展map
使用*:
的方式将一个map内联到另一个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]
assert map == [a:1, b:2, c:3, d:8]
由*:
方式内联进来的元素支持被覆盖。
8.2.范围运算符
Groovy支持使用..
来创建一个Range
def range = 0..5 // 从0到5的列表
assert (0..5).collect() == [0, 1, 2, 3, 4, 5]
assert (0..<5).collect() == [0, 1, 2, 3, 4] // 从0到4的列表
assert (0..5) instanceof List
assert (0..5).size() == 6
任何可比较对象都可以使用..
8.3.Spaceship运算符
<=>
是对compareTo
方法的代理
assert (1 <=> 1) == 0
assert (1 <=> 2) == -1
assert (2 <=> 1) == 1
assert ('a' <=> 'z') == -1
8.4.下标运算符
[]
是getAt
和putAt
的简写,取决于在赋值符号的左边还是右边调用。
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.安全索引运算符
Groovy3.0引入了安全索引运算符:?[]
, 和?.
相似。
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.成员运算符
成员运算符in
可以判断该列表中是否包含该值。等同于isCase
和列表的contais
方法。
def list = ['Grace','Rob','Emmy']
assert ('Emmy' in list)
assert list.isCase('Emmy')
assert list.contains('Emmy')
8.7.相等运算符
在Groovy使用==
判断是否相等和Java中不同,使用==
实际是调用了实例的equals
方法。
如果需要判断是否为相同引用,应该使用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.8.强转运算符
强转运算符as
是类型强转的变体,但不同于类型强转。
Integer x = 123
String s = (String) x // Integer不能强转成String,会抛出ClassCastException
Integer x = 123
String s = x as String
println s
assert "123" == s // 使用as成功把Integer转换成了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.9.尖括号运算符
尖括号运算符<>
是仅添加语法的糖运算符,以支持与Java 7中同名运算符的兼容性。它用于指示应从声明中推断出泛型类型:
List<String> strings = new LinkedList<>()
在动态Groovy中,这是完全未使用的。 在静态类型检查的Groovy中,它也是可选的,因为Groovy类型检查器会执行类型推断,无论是否存在此运算符。
8.10.Call运算符
()
运算符是隐式地调用call
方法。对于任意定义了call
方法的对象,可以省略.call
的部分,而直接使用Call运算符。
class MyCallable {
int call(int x) { // 在MyCallable中定义了call方法
2*x
}
}
def mc = new MyCallable()
assert mc.call(2) == 4 // 使用传统的调用方式调用call方法
assert mc(2) == 4 // 省略.call直接使用()运算符
9.运算符优先级
下面列出了Groovy 操作符的优先级
Level | Operator(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 | ` | ` |
12 | && |
logical and |
13 | ` | |
14 | ? : |
ternary conditional |
?: |
elvis operator | |
15 | = **= *= /= %= += -= <<= >>= >>>= &= ^= ` |
= ?=` |
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)
}
}
def b1 = new Bucket(4)
def b2 = new Bucket(11)
assert (b1 + b2).size == 15 // 因为实现了plus方法,所以可以使用+运算符
下面是运算符及其对应方法的完整列表:
Operator | Method | Operator | Method |
---|---|---|---|
+ |
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.and(b) | -- |
a.previous() |
^ |
a.xor(b) | +a |
a.positive() |
as |
a.asType(b) | -a |
a.negative() |
a() |
a.call() | ~a |
a.bitwiseNegate() |