您的位置:首页 >聚焦 >

Groovy之操作符

2022-05-15 05:49:13    来源:程序员客栈

这里对Groovy中常见的操作符进行介绍

abstract.png
操作符用法算术操作符

同Java一样,在算术操作符方面Groovy并无二致。示例代码如下所示

classOperatorDemo{staticvoidarithmetic(){//加assert3+4==7//加并赋值deffoo1=3foo1+=4assertfoo1==7//减assert4-3==1//减并赋值deffoo2=4foo2-=3assertfoo2==1//乘assert4*3==12//乘并赋值deffoo3=4foo3*=3assertfoo3==12//除assert12/3==4//除并赋值deffoo4=12foo4/=3assertfoo4==4//求余assert13%3==1//求余并赋值deffoo5=13foo5%=3assertfoo5==1//幂assert2**3==8//幂并赋值deffoo6=2foo6**=3assertfoo6==8//一元运算符+表示正数assert+3==3//一元运算符-表示负数assert-4==0-4assert-(-11)==11//后缀自增defa1=2defb1=a1++asserta1==3assertb1==2//前缀自增defa2=2defb2=++a2asserta2==3assertb2==3//后缀自减defa3=2defb3=a3--asserta3==1assertb3==2//前缀自减defa4=2defb4=--a4asserta4==1assertb4==1}}

关系运算符

Groovy的关系运算符示例如下所示

classOperatorDemo{staticvoidrelational(){//相等assert(3*4)==(10+2)//不相等assert3!=4//小于assert3<4//小于等于assert3<=4assert4<=4//大于assert5>4//大于等于assert5>=4assert5>=5}}

逻辑运算符

Groovy在逻辑运算符支持常见的与、或、非,同时具备短路求值的特点

classOperatorDemo{staticvoidlogical(){//逻辑与,支持短路求值asserttrue&&true//逻辑或,支持短路求值assertfalse||true//逻辑非assert!false}}

位运算

Groovy与Java一样,同样支持位运算。示例如下所示

classOperatorDemo{staticvoidbit(){//按位与inta=0b1010//a=10asserta==10intb=0b0110//b=6assertb==6intc=0b0010//c=2assertc==2assert(a&b)==c//按位或intd=0b1110//d=14assertd==14assert(a|b)==d//按位异或inte=0b1100asserte==12assert(a^b)==e//按位取反bytef=0b00001111assertf==15byteh=0b11110000asserth==-16assert(~f)==h//左移bytei=0b00000011asserti==3bytej=0b00001100assertj==12assert(i<<2)==j//右移assert(j>>2)==i//右移:左边使用原符号位进行填充,右边超出部分直接丢弃bytek=0b11111011assertk==-5bytep=0b11111110assertp==-2assert(k>>2)==p//无符号右移:左边使用0进行填充,右边超出部分直接丢弃intq=0x8022_11ff//数字支持使用下划线进行划分,便于人眼查看assertq==-2145250817intr=0x0080_2211assertr==8397329assert(q>>>8)==r}}

条件运算符

Groovy不仅提供了传统的三元运算符。还特别提供了Elvis运算符、Elvis赋值运算符。对于前者而言,如果Elvis运算符左边的操作数判定为真,则返回左边操作数; 否则返回右边的操作数;对于后者而言,Elvis赋值运算符, 其是对Elvis运算符的进一步简化, 省去了再次赋值操作。示例如下所示

classOperatorDemo{staticvoidconditional(){//三元运算符defa1=(2>1)?3:4asserta1==3defa2=(2>3)?3:4asserta2==4//Elvis运算符,如果?:运算符左边的操作数判定为真,则返回左边操作数;否则返回右边的操作数//可以视为简版的三元运算符defb1="HelloWorld"//等同于b1=(b1!=null||b1!="")?b1:"Hi"b1=b1?:"Hi"//非空字符串视为真assertb1=="HelloWorld"//Elvis运算符defb2=""b2=b2?:"Hi"assertb2=="Hi"//Elvis赋值运算符,其是对Elvis运算符的进一步简化,省去了再次赋值操作defc1="HelloWorld"c1?="Hi"assertc1=="HelloWorld"defc2=""c2?="Hi"assertc2=="Hi"}}

正则操作符

特别地,Groovy针对正则操作提供了相应的操作符。示例如下所示

classOperatorDemo{staticvoidregular(){Stringregex=/\S+\s+\S+///模式操作符~defpattern1=~regexassertpattern1instanceofPatterndeftext1="OneTwoThreeFourFive"defmatcher1=pattern1.matcher(text1)assertmatcher1instanceofMatcherassertmatcher1.size()==2assertmatcher1[0]=="OneTwo"assertmatcher1[1]=="ThreeFour"//查找运算符=~//具体地,其会在文本text1上应用正则表达式regex,生成matcherdefmatcher2=(text1=~regex)assertmatcher2instanceofMatcherassertmatcher2.size()==2assertmatcher2[0]=="OneTwo"assertmatcher2[1]=="ThreeFour"//匹配运算符==~,其是完整的匹配,而非部分匹配booleanb1="OneTwoThreeFour"==~regexbooleanb2="OneTwo"==~regexassertb1==falseassertb2==true}}

对象操作符

Groovy对于对象引用提供了丰富的操作符。需要特别提醒的是,在Java中对两个对象引用使用==操作符比较的是两个对象的地址是否一样;而在Groovy中==操作符用于比较两个对象的内容是否一样,事实上该操作符是通过equals方法实现的。当然Groovy自然也是支持比较两个对象的地址,其提供了===操作符。事实上该操作符是通过is方法实现的。示例代码如下所示

classOperatorDemo{staticvoidobject(){defperson1=newPerson("remark":"领军人才")//通过.操作符访问字段person1.name="Aaron"assertperson1.name=="Aaron"//通过.操作符修改字段实际上是隐式调用setter方法person1.age=18assertperson1.age==218//通过.操作符获取字段实际上是隐式调用getter方法assertperson1.remark==":领军人才"//通过.@运算符可以实现直接访问字段,而不是通过隐式调用getter、setter方法实现assertperson1.@remark=="领军人才"person1.@age=17assertperson1.age==17//安全引用操作符Personperson2=null//如果?.安全引用操作符的引用为null,则不会调用方法,而是直接返回null以避免NPEassertperson2?.getAge()==nullassertperson1?.getAge()==17//方法指针运算符MethodClosurefun1=person1.&getAge//方法指针的类型是闭包assertfun1instanceofClosureassertfun1()==17assertfun1.call()==17//方法指针同样支持多分派deffun3=person1.&test1assertfun3.call("Bye")=="Aaron:Bye"assertfun3.call(3)=="<17+3>-->>20"//可通过new获取构造器的方法指针deffun4=Person.&newPersonperson3=fun4.call("name":"Bob")assertperson3.name=="Bob"//通过类先获取非静态方法的方法指针deffun2=String.&toUpperCase//在执行闭包时,再传入该类的实例,以作为方法的调用者assertfun2.call("Hello")=="HELLO"deffun5=Person.&test1assertfun5.call(person1,"welcome")=="Aaron:welcome"//方法指针运算符同样适用于静态方法deffun6=String.&valueOfassertfun6.call(false)=="false"assertfun6.call(996)=="996"//Groovy对Java8的::方法引用运算符保持支持兼容deflist1=["71","2","4"].stream().map(Integer::valueOf).collect(Collectors.toList())assertlist1==[71,2,4]//list1a,list1b引用地址相同deflist1a=[1,2]asLinkedListdeflist1b=list1a//另外一个包含相同元素的列表deflist2=[1,2]asLinkedList//判断两个引用的内容是否相同//==运算符所对应的方法是equalsassertlist1a==list2assertlist1a.equals(list2)//类似地,!=运算符是对equals方法的结果进行否定assertlist1a!=[985,211]assert!(list1a.equals([985,211]))//判断两个引用的地址是否相同//===运算符对应is方法assertlist1a===list1aassertlist1a.is(list1b)//判断两个对象的引用地址是否不同//类似地,!==运算符是对is方法的结果进行否定assertlist1a!==list2assert!(list1a.is(list2))}}classPerson{StringnameIntegerageStringremarkvoidsetAge(Integerage){this.age=200+age}StringgetRemark(){return":$remark"}/***实现equals方法,实现重载==运算符*@paramother*@return*/@Overridebooleanequals(Objectother){if(!other||!(otherinstanceofPerson)||name!=other.name||age!=other.age||remark!=other.remark){returnfalse}returntrue}Stringtest1(Stringmsg){return"$name:$msg"}Stringtest1(Integernum){return"<${this.age}+${num}>-->>${this.age+num}"}}

其它

Groovy中其它部分常见的操作符,示例如下所示

classOperatorDemo{staticvoidother(){//飞船运算符,通过调用Comparable接口的compareTo方法进行比较//15==15assert(15<=>15)==0//44>22assert(44<=>22)==1//22<44assert(22<=>44)==-1//安全索引运算符?[],作用类似于?.安全引用操作符//避免由于数组为null而导致的NPEString[]array1=["Amy","Aaron"]array1?[1]="Bob"assertarray1?[0]=="Amy"assertarray1?[1]=="Bob"array1=null//array1为null,将不会应用索而是直接返回nullassertarray1?[0]==null//安全索引运算符同样适用于Mapdefmap1=[:]map1?["Aaron"]=18assertmap1?["Aaron"]==18map1=nullassertmap1?["Aaron"]==null//成员操作符deflist1=["MicroSoft","Apple","Xiaomi","FaceBook"]//判定Apple是否是list1的成员assert"Apple"inlist1//等效于调用isCase方法assertlist1.isCase("Apple")assert!("Huawei"inlist1)assert996inInteger//等效于调用isCase方法assertInteger.isCase(996)assert!(3.14finInteger)assert3.14finFloatassertFloat.isCase(3.1f)}}

操作符重载

在Groovy中,部分操作符是有对应的方法。换言之,通过操作符或方法调用在本质上效果是一致的。但操作符一旦有对应的方法,就为我们提供了另外一种编程方式,即进行操作符的重载。在特殊场景下,操作符的重载可以大大方便我们的使用。比如期望通过乘法符号计算两个矩阵的乘积,在Java中这显然是不可能。因为我们不能自定义乘法操作符的具体逻辑,而在Groovy中则可以在我们自定义的矩阵类中通过重载乘法操作符实现。这里给出Groovy可以进行重载的操作符及对应的方法名

figure 1.jpeg

下面给出一个进行重载操作符的示例,方便理解、使用

classOperatorOverLoad{staticvoidmain(String[]args){testPlus()testNext()testCall()}staticvoidtestNext(){Foodfood1=newFood("西瓜",14)food1++assertfood1.toString()=="Food{type=西瓜,num=15}"deffood2=newFood(type:"哈蜜瓜")food2++assertfood2.toString()=="Food{type=哈蜜瓜,num=1}"}staticvoidtestPlus(){Foodfood1=newFood("橘子",1)Foodfood2=newFood("橘子",2)Foodfood3=newFood("type":"橘子")Foodfood4=newFood("type":"苹果")assert(food1+food2).toString()=="Food{type=橘子,num=3}"assert(food2+food3).toString()=="Food{type=橘子,num=2}"assert(food1+food4)==null}staticvoidtestCall(){Foodfood1=newFood("柠檬",21)defstr=food1()//显式调用call方法assertfood1.call()=="Food{type=柠檬,num=21}"//通过调用运算符()调用call方法assertfood1()=="Food{type=柠檬,num=21}"}}classFood{StringtypeIntegernumFood(Stringtype,Integernum){this.type=typethis.num=num}Food(){}/***重载加法运算符+*@paramother*@return*/Foodplus(Foodother){if(!this.type||this.type!=other?.type){returnnull}Integernum1=this.num?:0Integernum2=other.num?:0Integerresult=num1+num2returnnewFood("type":type,"num":result)}/***重载自增运算符++*@return*/Foodnext(){this.num?=0this.num++returnthis}/***重载调用运算符()*@return*/Stringcall(){returntoString()}@OverrideStringtoString(){return"Food{type=$type,num=$num}"}}

参考文献Groovy In Action · 2nd Edition Dierk König、Guillaume Laforge著

关键词: 领军人才 赋值运算符 是否相同

相关阅读