操作符
Groovy中有算术运算符
、关系运算符
、逻辑运算符
、位运算符
、条件运算符
、对象运算符
和其他运算符
。
算术运算符
Groovy的算术运算符
包括+
(加)、-
(减)、*
(乘)、/
(除)、%
(取模)和**
(幂运算)。
-
(负号)有时也表示取反。
> println(1 + 2 == 3)
true
> println(4 - 3 == 1)
true
> println(3 * 5 == 15)
true
> println(3 / 2 == 1.5)
true
> println(10 % 3 == 1)
true
> println(2 ** 3 == 8)
true
> println(-(-1) == 1)
true
其中-
和*
还能用于字符串的运算。
> def line = "Hello World"
> def hello = line - " World"
> println(hello)
Hello
> def hello2 = hello * 2
> println(hello2)
HelloHello
这六种算术运算符都有相应的算术赋值运算符。
> a = 1
> println(a += 1)
> println(a++)
> println(++a)
2
2
4
> b = 1
> println(b -= 1)
> println(b--)
> println(--b)
0
0
-2
> c = 2
> println(c *= 2)
4
> d = 4
> println(d /= 2)
2
> e = 10
> println(e %= 3)
1
> f = 2
> println(f **= 2)
4
关系运算符
Groovy的关系运算符
有八种:==
、!=
、<
、<=
、>
、>=
、===
和!==
。
除了===
和!==
这两种之外,其他的Java都有。
而===
和!==
的作用在于比较对象是否相等,它们底层都会调用is()
方法。
> import groovy.transform.EqualsAndHashCode
> @EqualsAndHashCode
> class Person { String name }
> def lixingyun = new Person(name: 'lixingyun')
> def copyPerson = lixingyun
> def wanglin = new Person(name: 'lixingyun')
// lixingyun和wanglin有相同的hashcode
> println(lixingyun.equals(wanglin))
true
// lixingyun和copyPerson是同一个对象,不仅hashcode相同,内存地址也相同
> println(lixingyun === copyPerson)
true
// is()是===调用的底层方法
> println(lixingyun.is(copyPerson))
true
// lixingyun和wanglin不是同一个对象,虽然hashcode相同,但内存地址不同
> println(lixingyun !== wanglin)
true
> println(!lixingyun.is(wanglin))
true
逻辑运算符
所有的编程语言,常用逻辑运算符都是那三个:&&
(与
)、||
(或
)和!
(非
)。
// 逻辑与:只要有一个为false,那么表达式的结果就是false
> println(true && false)
false
// 逻辑或:只要有一个为true,那么表达式的结果就是true
> println(true || false)
true
// 逻辑非:负负得正
> println(!!false)
false
位运算符
因为计算机中的数据都是由二进制的0
和1
组成的,所以位运算就是操作这些数字位上的0
和1
。
Groovy有四种位运算符:&
(按位与
)、|
(按位或
)、^
(按位异或
)和~
(按位取反
)。
> int a = 0b00101010
> println(a == 42)
true
> int b = 0b00001000
> println(b == 8)
true
// &运算:0&0 = 0,0&1 = 0,1&0 = 0,1&1 = 1
> println((a & a) == a)
true
> println((a & b) == b)
true
// |运算:0|0 = 0,0|1 = 1,1|0 = 1,1|1 = 1
> println((a | a) == a)
true
> println((a | b) == a)
true
> int mask = 0b11111111
// ^运算:0^0 = 0,0^1 = 1,1^0 = 1,1^1 = 0
> println(((a ^ a) & mask) == 0b00000000)
true
> println(((a ^ b) & mask) == 0b00100010)
true
// ~运算:~0 = 1,~1 = 0
> println(((~a) & mask) == 0b11010101)
true
除了对两个数字的位进行操作,还能对单个数字位进行移动
,这种操作常在数据加密时出现。
// 数据 << 向左移动的位数
> println(8.equals(2 << 2))
true
> println(16L.equals(2L << 3))
true
> println(32G.equals(2G << 4))
true
// 数据 >> 向右移动的位数
> println(-1 == -128 >> 8)
true
> println(2G == 5G >> 1)
true
> println(-3G == -5G >> 1)
true
// 无符号右移8位
> println(16777215 == -128 >>> 8)
true
条件运算符
所谓条件运算符,其实就是逻辑运算符的另一个名称,只不过条件运算符中又新加入了一个叫做三元运算符
的东西。
> flag = true
// 条件 ? 为ture时执行 : 为false时执行
> println(flag ? 'ok' : 'not ok')
ok
// 当条件和true分支的数值相等时,true部分可以省略
> println(flag ?: 'not ok')
true
> string = 'Groovy'
> println(string ? 'Groovy' : 'Not Groovy')
Groovy
> println(string ?: 'Not Groovy')
Groovy
在Groovy3
版本之后加入了一个称为with{}
的条件运算符。
> import groovy.transform.ToString
> @ToString
> class Person {
> String name
> int age
> static void main(String[] args) {
> def he = new Person(age: 20)
> he.with {
> name = name ?: 'lixingyun' // 如果name为空,就改为'lixingyun'
> age = age ?: 19 // 如果age为空,就改为19
> }
> println(he.toString())
> }
> }
Person(lixingyun, 20)
对象运算符
对象安全导航符?
> class Person {
> String name
> int id
> static void main(String[] args) {
> def person1 = new Person(id:1)
> def name = person1.name
> // 输出null,因为没有给name赋值
> println(name)
> // 通过外部传入
> Person person2 = null
> // 这样写会出现NullPointerException异常
> // def value1 = person2.name
> // println(value1)
> // 但这样写就不会出现异常了,而是直接给name赋值为null
> def value2 = person2?.name
> println(value2)
> }
> }
null
null
直接字段访问符$
> class Person {
> String name
> Person(String name) {
> this.name = name
> }
> // 重构了get方法,仅对get有效
> String getName() {
> "名称: $name"
> }
> static main(String[] args) {
> Person person = new Person("lixingyun")
> // 两种方式都可以访问成员变量
> println(person.getName())
> println(person.name)
> }
> }
名称: lixingyun
名称: lixingyun
方法指针运算符&
> class Test {
> def static main(String[] args) {
> def str = 'java.lang.String'
> def res = str.&toUpperCase // 将字符串中的字母转为大写
> def upper = res()
> println(upper)
> println(str.toUpperCase())
> }
> }
JAVA.LANG.STRING
JAVA.LANG.STRING
既然str.toUpperCase()
和str.&toUpperCase
的作用相同,为什么还要弄一个指针呢?
因为指针相当于句柄
,相当于一个对操作的描述
,它可以实现操作的延时执行,在需要它的地方再进行功能的处理。
> class Person {
> String name
> int age
> // 闭包转换为列表元素
> def transform(List elements, Closure action) {
> def result = []
> elements.each {
> // 执行闭包调用
> result << action(it)
> }
> result
> }
> String describe(Person person) {
> "$person.name $person.age 岁"
> }
> static main(String[] args) {
> Person person = new Person()
> // 将方法的调用逻辑作为Closure对象,相当于拿到了数据的操作方法,到哪里去执行都可以
> Closure action = person.&describe
> List list = [
> new Person(name: 'lixingyun', age: 19),
> new Person(name: 'wanglin', age: 20)
> ]
> def tranList = person.transform(list, action)
> println(tranList)
> // 效果和前面一样
> for (Person p in list) {
> println(p.describe(p))
> }
> }
> }
[lixingyun 19 岁, wanglin 20 岁]
lixingyun 19 岁
wanglin 20 岁
方法引用运算符::
这里所说的方法引用
就是Java函数式编程中指的方法引用
。
> import static java.util.stream.Collectors.toList
// 类的实例方法引用:add()是BigInteger类的实例方法
> println([1g, 2g, 3g].stream().reduce(0g, BigInteger::add))
6
// 对象实例方法引用:add()是对象3G的实例方法
> println([1G, 2G, 3G].stream().map(3G::add).collect(toList()))
[4, 5, 6]
// 类的静态方法引用:valueOf()是BigInteger类的静态方法
> println([1L, 2L, 3L].stream().map(BigInteger::valueOf).collect(toList()))
[1, 2, 3]
// 对象静态方法引用:valueOf()是对象3G的静态方法
> println([1L, 2L, 3L].stream().map(3G::valueOf).collect(toList()))
[1, 2, 3]
// 类的静态方法引用:valueOf()是Integer类的静态方法
> println(['1', '2', '3'].stream().map(Integer::valueOf).collect(toList()))
[1, 2, 3]
// 数组的构造器引用:new是数组的构造器
> println([1, 2, 3].stream().toArray(Integer[]::new))
[1, 2, 3]
正则表达式运算符
模式运算符
Groovy的模式运算符~
提供了一种创建java.util.regex.Pattern
实例的简单方法。
> import java.util.regex.Pattern
> def pattern = ~/foo/
> println(pattern instanceof Pattern)
true
> pattern = ~'foo'
> println(pattern instanceof Pattern)
true
> pattern = ~'''foo'''
> println(pattern instanceof Pattern)
true
> pattern = ~"foo"
> println(pattern instanceof Pattern)
true
> pattern = ~"""foo"""
> println(pattern instanceof Pattern)
true
> pattern = ~$/ slashy $ string /$
> println(pattern instanceof Pattern)
true
查找运算符
可以通过=~
直接创建java.util.regex.Matcher
的实例。
> import java.util.regex.Matcher
> def text = "some text to match"
> def match1 = text =~ /match/
> def match2 = text =~ /wow/
> println(match1.find())
true
> println(match1.findResult())
match
> println(match1 instanceof Matcher)
true
> println(match2.find())
false
> println(match2.findResult())
null
> println(match2 instanceof Matcher)
true
匹配运算符
匹配运算符==~
是查找运算符的一个微小变体,它不返回java.util.regex.Matcher
的实例,而是返回布尔值,并且需要严格匹配输入字符串。
> import java.util.regex.Matcher
> def text1 = "some text to match"
> def text2 = "match"
> def match1 = text1 ==~ /match/
> def match2 = text2 ==~ /match/
> println(match1)
fasle
> println(match1.find())
null
> println(match1.findResult())
false
> println(match1 instanceof Matcher)
false
> println(match1 instanceof Boolean)
true
> println(match2)
true
> println(match2.find())
true
> println(match2.findResult())
true
> println(match2 instanceof Matcher)
false
> println(match2 instanceof Boolean)
true
比较和匹配运算符
这是把前面的两种运算符融合到了一起:当模式匹配单个精确值时就使用匹配运算符,否则就使用查找运算符。
> println('two words' ==~ /\S+\s+\S+/)
true
> println('two words' ==~ /^\S+\s+\S+$/)
true
// 不匹配
> println(!(' leading space' ==~ /\S+\s+\S+/))
true
// 单个匹配
> def m1 = 'two words' =~ /^\S+\s+\S+$/
> println("m1.findResult() = " + m1.findResult())
m1.findResult() = two words
// 不匹配
> def m2 = 'now three words' =~ /^\S+\s+\S+$/
> println("m2.findResult() = " + m2.findResult())
m2.findResult() = null
// 单个匹配
> def m3 = 'now three words' =~ /\S+\s+\S+/
> println("m3.findResult() = " + m3.findResult())
m3.findResult() = now three
// 单个匹配
> def m4 = ' leading space' =~ /\S+\s+\S+/
> println("m4.findResult() = " + m4.findResult())
m4.findResult() = leading space
// 多个匹配
> def m5 = 'and with four words' =~ /\S+\s+\S+/
> println("m5.findAll() = " + m5.findAll())
m5.findAll() = [and with, four words]
> println(m5[0] == 'and with')
true
> println(m5[1] == 'four words')
true
其他运算符
点运算符*.
点运算符(*.
)用于将所有指定对象的指定项收集到列表中。
例如,如果需要把某个对象列表中所有对象的name
字段收集到列表中,就可以用到点运算符(*.
)。
> class Person {
> String name
> int age
> }
> def persons = [
> new Person(name: 'lixingyun', age: 19),
> null,
> new Person(name: 'wanglin', age: 20)
> ]
// 即使persons中存在null值,*.也不会抛出NullPointerException
> println(persons*.name)
[lixingyun, null, wanglin]
点运算符(*.
)还能嵌套使用。
> import groovy.transform.Canonical
> class Person {
> String name
> List<City> cities
> }
> @Canonical
> class City {
> String name
> }
> def persons = [
> new Person(name: 'lixingyun', cities: [new City('大唐'), new City('娆疆')]),
> new Person(name: 'wanglin', cities: [new City('洞府界'), new City('仙罡大陆')])
> ]
> def names = persons*.name
> println(names)
[lixingyun, wanglin]
// 嵌套调用
> def cities = persons*.cities*.name
> println(cities)
[[大唐, 娆疆], [洞府界, 仙罡大陆]]
> println(cities.sum())
[大唐, 娆疆, 洞府界, 仙罡大陆]
> println(cities.flatten())
[大唐, 娆疆, 洞府界, 仙罡大陆]
如果都是集合,那么可以用collectNested
来代替点运算符(*.
)。
> class Person {
> String name
> String city
> }
> def persons = [
> [new Person(name: 'lixingyun', city: '娆疆'), new Person(name: 'wanglin', city: '洞府界')],
> [new Person(name: 'xiaoyan', city: '中州'), new Person(name: 'lindong', city: '天玄大陆')]
> ]
> def cities = persons.collectNested{ it.city }
> println(cities)
[[娆疆, 洞府界], [中州, 天玄大陆]]
点运算符(*.
)中的*
还有下面一些用途。
> int function(int x, int y, int z) {
> x * y + z
> }
> def args1 = [4, 5, 6]
// 展开参数,逐一参与运算
> println("${function(*args1)}" == 26)
true
> def args2 = [2]
> println("${function(*args2, 5, 6)}" == 16)
true
> def items = [4, 5]
// 融入到list
> def list = [1, 2, 3, *items, 6]
> println(list == [1, 2, 3, 4, 5, 6])
true
> def m1 = [c: 3, d: 4]
// 融入到map
> def map = [a: 1, b: 2, *: m1]
> println(map == [a: 1, b: 2, c: 3, d: 4])
true
> def m2 = [c: 3, d: 4]
// 同上,但不会取代已有的kv键值对
> map = [a: 1, b: 2, *: m1, d: 8]
> println(map == [a: 1, b: 2, c: 3, d: 8])
true
范围运算符..
这个范围运算符(..
)和Go语言的切片实在是很像。
> println((0..5).collect() == [0, 1, 2, 3, 4, 5])
true
> println((0..<5).collect() == [0, 1, 2, 3, 4])
true
> println((0<..5).collect() == [1, 2, 3, 4, 5])
true
> println((0<..<5).collect() == [1, 2, 3, 4])
true
> println((0..5) instanceof List)
true
> println((0..5).size() == 6)
true
// 范围操作同样适用于这种有序的字符列表
> println(('a'..'d').collect() == ['a','b','c','d'])
true
> println(('a'..<'d').collect() == ['a','b','c'])
true
> println(('a'<..'d').collect() == ['b','c','d'])
true
> println(('a'<..<'d').collect() == ['b','c'])
true
排序运算符<=>
这是一种compareTo()
比大小方法的另类调用方式,它默认是按降序排列的,也就是说它是这样比大小的。
如果前后相等就返回
0
。如果前面比后面小就返回
-1
。如果前面比后面大就返回
1
。
> println((1 <=> 1) == 0)
true
> println((1 <=> 5) == -1)
true
> println((5 <=> 1) == 1)
true
// 对于这种有序的字符比较,同样有效
> println(('1' <=> '10') == -1)
true
> println(('3' <=> '-1') == 1)
true
> println(('a' <=> 'c') == -1)
true
下标运算符[x]
下标运算符([x]
)是getAt()
或putAt()
的简写符号。
> def list = [0, 1, 2, 3, 4]
> // 这一行和下一行等效:println(list.getAt(2) == 2)
> println(list[2] == 2)
true
> // 这一行和下一行等效:list.putAt(2, 4)
> list[2] = 4
> // 这一行和下一行等效:println(list.getAt(0..2) == [0, 1, 4])
> println(list[0..2] == [0, 1, 4])
true
> // 这一行和下一行等效:list.putAt(0..2, [6, 6, 6])
> list[0..2] = [6, 6, 6]
> println(list == [6, 6, 6, 3, 4])
true
也可以通过自定义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")
> }
> }
// 通过自定义getAt和putAt实现了功能重载
> def user = new User(id: 1, name: 'lixingyun')
> println(user[0] == 1)
true
> println(user[1] == 'lixingyun')
true
> user[1] = 'wanglin'
> println(user.name == 'wanglin')
true
安全索引运算符?[]
在Groovy3.0.0
中引入了安全索引运算符(?[]
)。
> String[] array = ['a', 'b']
> println(array?[1])
a
> array?[1] = 'c'
> println(array)
[a, c]
> array = null
> println(array?[1])
null
> array?[1] = 'c'
> println(null == array?[1])
true
> def personInfo = [name: 'lixungyun', city: 'raojiang']
> println('lixungyun' == personInfo?['name'])
true
> personInfo?['name'] = 'wanglin'
> println('wanglin' == personInfo?['name'])
true
> personInfo = null
> println(null == personInfo?['name'])
true
> personInfo?['name'] = 'wanglin'
> println(null == personInfo?['name'])
true
成员运算符in
成员运算符(in
)在List
的上下文中相当于调用contains()
或isCase()
方法,用来判断某个元素是否在列表中。
> list = ['lixingyun', 'wanglin', 'xiaoyan']
> println('lixingyun' in list)
true
> println('lindong' !in list)
true
身份运算符is
身份运算符(is
)可以用于比较引用的内存地址是否相等,这相当于调用===
或!==
运算符。
> def list1 = ['lixingyun']
> def list2 = ['lixingyun']
> println(list1 == list2)
true
> println(!list1.is(list2))
true
> println(list1 !== list2)
true
强制运算符as
当执行类型转换的时候,使用强制运算符(as
)可以避免类型转换异常。
> String input = '42'
// Integer num = (Integer) input 如果这样执行会抛出GroovyCastException
// 但这样就不会报错
> Integer num = input as Integer
> println(num)
1
而通过自定义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 forced into $target")
> }
> }
> def user = new User(name: 'lixingyun')
// 通过自定义的asType,将User转换为Identifiable
> def person = user as Identifiable
> println(person instanceof Identifiable)
true
> println(!(person instanceof User))
true
调用运算符()
这是一种调用方式的约定:当调用一个实例的方法而不指定方法名时,那么它就是默认在调用一个名为call()
的方法。
反过来说,如果方法中有call()
方法,就可以通过()
直接调用而省略掉方法名。
> class MyCallable {
> int call(int x) {
> 2 * x
> }
> }
> def mc = new MyCallable()
> println(mc.call(2) == 4)
true
// mc.call(2)等同于mc(2)
> println(mc(2) == 4)
true
运算符重载
Groovy允许重载各种运算符,以便它们可以与自定义的类配套使用。
> class Bucket {
> int size
> Bucket(int size) { this.size = size }
> Bucket plus(Bucket other) {
> return new Bucket(this.size + other.size)
> }
> }
> def b1 = new Bucket(1)
> def b2 = new Bucket(2)
// (b1 + b2).size 是 b1.plus(b2).size的简写形式
// Groovy会自动识别并应用类中的重载方法plus()
> println((b1 + b2).size == b1.plus(b2).size)
true
感谢支持
更多内容,请移步《超级个体》。