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