11. Groovy 运算符-区间运算符,下标运算符,钻石运算符等学习

2022-12-07 18:28:55 浏览数 (1)

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接口。

同时创建区间时也可以创建开区间。通过<符号。示例如下:

代码语言:javascript复制
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

三路比较运算符(别称宇宙飞船运算符),它的命名和它的符号有很大关系,标识符为:<=>。具体实现示例如下:

代码语言:javascript复制
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

下标运算符是getAtputAt方法的缩写。我们使用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进行比较。示例如下:

代码语言:javascript复制
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关键字实现强制转换:

代码语言:javascript复制
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中同名运算符的兼容性。它用于指示泛型类型应该从声明中推断出来:

代码语言:javascript复制
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。

下一篇介绍各种运算符的优先级知识点

0 人点赞