1.介绍
Groovy学习的第11篇内容。继续分享关于运算符的知识。本篇将会分享比较多的几种运算符的使用,主要为:区间运算符..
,三路比较运算符(别称宇宙飞船运算符,<=>
),下标运算符[]
,索引安全运算符?[]
,成员运算符(关键字in
),恒等运算符==
,强制运算符as
,钻石运算符<>
和调用运算符()
。
我的所有示例代码。都可以直接在VS 编译器中直接运行。需要安装有:Groovy插件和Code Runner 插件。(本地环境配置了Groovy SDK地址,Java SDK 地址)
2. 区间运算符-Range operator
Groovy支持范围的概念,并提供了一个符号:..
来创建对象范围。而针对这个范围,通常使用区间来进行定义。
在现在比较流行的高级语言中,都有对区间的支持。只是有些将这个定义为语法特性,在Groovy文档中,将区间定义放在了运算符的介绍目录中了。
具体示例如下:
代码语言:javascript复制def range = 1..10 //创建了一个1到10的区间对象。
println(range) //直接打印 输出:1..10
println(range.collect()) //输出[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
println(range.size()) //输出10
assert (range) instanceof List //能够通过断言检测
同上面的示例就可以看到了。区间定义的实际上是一个继承List的对象。是一个闭区间
在Groovy中,区间对象为:groovy.lang.Range
它继承了List接口。
同时创建区间时也可以创建开区间。通过<
符号。示例如下:
def range = 1..5 //创建1到5的区间
println(range.collect()) //输出 [1,2,3,4,5]
range =1<..5 //创建大于1等于5的区间
println(range.collect()) //输出 [2,3,4,5]
range =1<..<5 //创建大于1小于5的区间
println(range.collect()) //输出 [2,3,4]
区间的实现是轻量级的,可以从任何具有next()
和previous()
方法的Comparable
对象创建一个区间。也就是说不止int可以创建区间。
例如字符串区间:
代码语言:javascript复制def range ='a'..'d'
println(range.collect()) //输出 [a, b, c, d]
range ='a'<..'d'
println(range.collect()) //输出 [b, c, d]
range ='a'<..<'d'
println(range.collect()) //输出 [b, c]
3. 三路比较运算符-Spaceship operator
三路比较运算符(别称宇宙飞船运算符),它的命名和它的符号有很大关系,标识符为:<=>
。具体实现示例如下:
def x =1
def y =2
println( x <=> y) //输出结果为-1
因为它的比较运算符实际上是实现了compareTo
的比较值输出。
在CompartTo
的比较关系中。
- 左右两边相等:返回0。
- 左边小于右边:返回-1。
- 左边大于后边:返回1。
我们可以通过三路比较运算符简写compareTo
的比较运算而已。所以,三路比较运算符输出的值永远都只会有-1,0,1这三种情况。
让我们通过示例加深一下印象,示例如下:
代码语言:javascript复制//整数的比较
println(1<=>2) //输出-1 。因为左边小于右边
println(2<=>1) //输出1 ,因为左边大于右边
println(2<=>2) //输出0 ,因为左右两边相等
//字符串的比较
println('zin'<=>'yan') //输出 1 因为左边字符串转code后比右边大
println('a'<=>'b')// 输出-1, 因为a转code后比b要小。
4. 下标运算符-Subscript operator
下标运算符是getAt
或putAt
方法的缩写。我们使用List获取参数时,使用的[int]
就是下标运算符了。
示例代码如下:
代码语言:javascript复制def list=['zin','yan','z同学'] //创建一个字符串List
println(list.getAt(1)) //输出 yan
println(list[1]) //输出也是yan, 这种就叫做下标运算符
我们直接通过下标运算符省略了get写法而已。在上面的场景下下标运算符就是getAt的简写。
代码语言:javascript复制def list=['zin','yan','z同学'] //创建一个字符串List
list.putAt(2,'com') //例如通过putAt 将下标为2的元素进行修改
println(list.getAt(2)) //通过getAt获取的值。输出为:com
而我们通过下标运算符缩写putAt就可以写为:
代码语言:javascript复制def list=['zin','yan','z同学']
list[2]='com'
println(list[2]) //通过getAt获取的值。输出为:com
这也是官方文档中介绍的,下标运算符是是getAt
还是putAt
得根据该赋值操作是在等号的左边还是右边来决定的。
如果是在右边,那么就是getAt,如果是在左边那么就是putAt了。
PS:Java中集合对象是没有这个写法的。所以Groovy文档中才会专门介绍了下标运算符。
5. 索引安全运算符-Safe index operator
在Groovy 3.0开始,引入了索引安全运算符?[]
它的作用和?.
是类似的。主要也是用来避免Null值造成的程序异常。
而?.
是在引用对象时进行Null过滤,?[]
就是在下标运算符使用时判断集合或者数组对象是否为Null。(ps:不是判断里面的item是否为Null哦)
PS:Groovy分的很细啊,这些运算符之类的信息。
具体示例如下所示:
代码语言:javascript复制String[] array = null //创建一个空数组
//println(array[1]) //这个写法会提示Null值异常,NullPointerException错误
println(array?[1]) //这个写法会输出 null
def list=null
println(list[2]) //会提示NullPointerException错误
println(list?[2]) //会输出null
6. 成员运算符-Membership operator
成员运算符,也叫做隶属运算符。使用关键字:in
来表现。在普通对象中它相当于调用isCase
方法。在List中它相当于调用contains
方法。
示例如下:
代码语言:javascript复制def list = ['zin','yan','Z同学']
def s = 'zin' in list //
println(s) //输出:true
def x = 'com' !in list
println(x) //输出 :true
//上面的示例等效于下面的:
println(list.contains('zin')) //输出 true
println(!list.contains('com')) //输出true
结合示例,我们就能比较清晰的弄明白in关键字的作用了。它就是用来检测List等集合对象中是否包括。如果包括就返回true,不包括返回false
7. 恒等运算符-Identity operator
恒等运算符也叫做恒等算式。使用的标识符为:==
。在Groovy中,使用==
测试相等性不同于在Java中使用相同的运算符。在Groovy中,它调用equals
。如果要比较引用相等性,应使用is
进行比较。示例如下:
def list1 = ['zinyan','z同学']
def list2 = ['zinyan','z同学']
println(list1 == list2) //输出 true
println(list1.is(list2)) //输出 false 比较两个对象的引用
println(list1 !== list2) //输出 true
!==
运算符在第六篇https://zinyan.com/?p=394 关系运算符中有过介绍。
在这里再次复习一遍,它比较的是两个对象的引用。同时必须是Groovy 3.0.0版本才能使用。老版本建议使用is
进行比较吧。
在Groovy中 的is运算符才等同于java中的
==
比较符。这中间是有差异的
8. 强制运算符-Coercion operator
在Groovy中使用as表示强制转换。强制将对象从一种类型转换为另一种类型,但它们与赋值不兼容。在Java中的强制转换通常是写作:
代码语言:javascript复制Yan x =new Yan();
Zinyan z =(Zinyan) x;
上面如果两者之间如果不能正确转换的话,我们在开发过程中不知道。只有运行状态才会报错ClassCastException
。
为了避免这个错误。Groovy通过 as
关键字实现强制转换:
Integer x= 123
String s = x as String
println(s) //输出123
//我们如果将S 强制转换为int
x = s as Integer
println(x) //输出123
在上面的示例中,两个参数能够正确互相转换。
当一个对象被强制转换为另一个对象时,除非目标类型与源类型相同,否则强制将返回一个新对象。
强制规则因源和目标类型而异,如果找不到转换规则,强制可能会失败。例如:
代码语言:javascript复制String s ="zinyan.com"
//我们如果将S 强制转换为int
Integer y = s as Integer
println(y)
就会提示错误:Caught: java.lang.NumberFormatException: For input string: "zinyan.com"
这是因为强制转换时找不到规则了。但是其实强制转换是调用了类的asType
方法。我们可以通过重构该方法,实现自定义转换规则。
让as
强制转换的逻辑按照我们定义的规则进行转换。
示例代码如下:
代码语言:javascript复制//自定义类
class Identifiable {
String name
}
//自定义类
class User {
Long id
String name
//重构asType方法
def asType(Class target) {
if (target == Identifiable) { //如果要转换的类是Identifiable类
return new Identifiable(name: name) //那么就将它的name值进行复制即可
}//否则返回 转换失败异常
throw new ClassCastException("User cannot be coerced into $target")
}
}
def u = new User(name: 'zinyan')
def p = u as Identifiable
println(p.name) //输出:zinyan
转换过程中,其实就是创建了一个新的对象并进行赋值。这种可以优化很多样板代码。
9. 钻石运算符-Diamond operator
钻石运算符<>
是一个语法上的纯糖运算符,用于支持与Java 7中同名运算符的兼容性。它用于指示泛型类型应该从声明中推断出来:
List<String> strings = new LinkedList<>()
在动态Groovy中,这是完全未使用的。在静态类型检查的Groovy中,它也是可选的,因为无论是否存在该运算符,Groovy type checker都会执行类型推断。
PS:所以这个就是java提供的一种纯糖运算符。Groovy中可以写,也可以不写。
10. 调用运算符-Call operator
调用运算符 ()
就是为了隐式调用名为call
的方法。只要实现了call方法的对象,都可以直接使用()
进行调用。
示例代码如下:
代码语言:javascript复制//创建一个类
class MyCallable {
//自定义一个call方法
int call(int x) {
2*x
}
}
def mc = new MyCallable()
println(mc.call(202)) //返回:404
println(mc(202)) //返回:404
通过示例代码就可以看出来了mc()
和 mc.call()
是等效的。
11. 小结
通过本篇的学习,对于Groovy中的很多语法有了更多的理解。因为上面的这些语法,在实际Groovy脚本的使用中非常频繁。
当我们弄明白每个语法的含义时,我们写Groovy时就会更简单,清晰,明了。
本篇内容参考于Groovy官方文档:http://docs.groovy-lang.org/docs/groovy-4.0.6/html/documentation/#_range_operator。
下一篇介绍各种运算符的优先级知识点