日积月累--Groovy语言规范之操作符

时间:2022-04-29 17:49:39

这篇文章介绍的是Groovy编程语言的操作符,不知为何Groovy将其单独列为了一个部分。
关于操作符的官方文档地址:(http://groovy-lang.org/operators.html)

再次说明一下:本文不是对官方文档的完完整整翻译,而是掺杂了一些个人的理解,使其更加本土化。关于Groovy的一些语法,可参照我的另一篇文章(http://blog.csdn.net/dabaoonline/article/details/50469704);

1. 算术运算符Arithmetic operators

同Java语言一样Groovy支持同样的算术运算符。

1.1. 普通算术运算符Normal arithmetic operators

以下这些二元算术运算符在Groovy中是适用的:

Operator Purpose Remarks
+ addition加
- subtraction减
* multiplication乘
/ division除 对于整数相除使用intdiv()方法,详情查看integer division
% modulo
** power 更多关于返回值的信息,点击 the power operation

举一些例子:
assert 1 + 2 == 3
assert 4 - 3 == 1
assert 3 * 5 == 15
assert 3 / 2 == 1.5
assert 10 % 3 == 1
assert 2 ** 3 == 8

这个没啥难点,主要是关于除以和幂。整数的除以得到的结果和java中有所不同,如若想达到java中的效果,需要使用intdiv()方法,具体参考:我的另一篇文章(http://blog.csdn.net/dabaoonline/article/details/50469704);

1.2. 一元运算符Unary operators

共三个+,-,++,–这个和Java中相同
例子:

def a = 2
def b = a++ * 3

assert a == 3 && b == 6

def c = 3
def d = c-- * 2

assert c == 2 && d == 6

def e = 1
def f = ++e + 3

assert e == 2 && f == 5

def g = 4
def h = --g + 1

assert g == 3 && h == 4

1.3. 赋值运算符Assignment arithmetic operators

我们上面看到的二元算术运算符同样可以用作赋值运算符:

  • +=
  • -=
  • *=
  • /=
  • %=

来看看它们的表现:
def a = 4
a += 3
assert a == 7
def b = 5
b -= 3
assert b == 2
def c = 5
c *= 3
assert c == 15
def d = 10
d /= 2
assert d == 5
def e = 10
e %= 3
assert e == 1
def f = 3
f **= 2
assert f == 9

2. 关系运算符Relational operators

关系运算符允许两个对象间的比较,以确定其二者是否相同,或者用于判断那个大,那个小,还是相等。
以下操作符是适用的:

Operator Purpose
== equal
!= different
< less than
<= less than or equal
> greater than
>= greater than or equal

举几个它们之间比较的例子:
assert 1 + 2 == 3
assert 3 != 4
assert -2 < 3
assert 2 <= 2
assert 3 <= 4
assert 5 > 1
assert 5 >= -2

3.逻辑运算符 Logical operators

Groovy提供了三个用于布尔表达式的逻辑运算符:

  • &&: logical “and”
  • ||: logical “or”
  • !: logical “not”

通过几个例子阐述一下:
assert !false
assert true && true
assert true || false

这里需要说明的是Groovy中的&&、||与Java中的含义相同。
另外除了这些逻辑运算符,Groovy中还有&,|均和Java中含义相同。

3.1.优先级 Precedence

“非”的优先级大于”且”

assert (!false && false) == false 

“且”的优先级高于”或”

assert true || true && false  

3.2. 短路Short-circuiting

这些知识同Java,短路的目的说白了就是效率高一些。
什么是短路?
1. 逻辑与(&&),如果第一个操作数为false,那么结果肯定false,所以在这种情况下,将不会执行逻辑与后面的运算了,即发生了短路。
2. 逻辑或(||),如果第一个操作数为ture,那么结果肯定是ture,所以在这种情况下,将不会执行逻辑与后面的运算了,即发生了短路。

boolean checkIfCalled() {   
called = true
}

called = false
true || checkIfCalled()
assert !called

called = false
false || checkIfCalled()
assert called

called = false
false && checkIfCalled()
assert !called

called = false
true && checkIfCalled()
assert called

4.位运算符 Bitwise operators

同Java:
Groovy提供了4中位运算符:

&: bitwise “and”
|: bitwise “or”
^: bitwise “xor” (exclusive “or”)
~: bitwise negation

位运算符可以被应用答byte或int类型的运算,且返回值为int类型:
上例子:
int a = 0b00101010
assert a==42
int b = 0b00001000
assert b==8
assert (a & a) == a
assert (a & b) == b
assert (a | a) == a
assert (a | b) == a
int mask = 0b11111111
assert ((a ^ a) & mask) == 0b00000000
assert ((a ^ b) & mask) == 0b00100010
assert ((~a) & mask) == 0b11010101

It’s worth noting that the internal representation of primitive types follow the Java Language Specification. In particular, primitive types are signed, meaning that for a bitwise negation, it is always good to use a mask to retrieve only the necessary bits.
值得注意的是基本类型在Java语言规范中的表示方式,Java内部以补码的形式表示二进制数。特别的,基本数据类型都是有符号的,这就意味着对于一个按位取反的数,使用mask来获取需要的位是很有用的。
In Groovy, bitwise operators have the particularity of being overloadable, meaning that you can define the behavior of those operators for any kind of object.
在Groovy中,按位操作符具有重载的特殊性,这就意味着你可以定义操作符来对任意对象进行操作。
注:这点操作符重载非常特别,是因为Java不具有这一特性,开始理解起来比较难一些,后续还会细讲

5. 条件运算符Conditional operators

5.1. Not操作符 Not operator

The “not” operator is represented with an exclamation mark (!) and inverts the result of the underlying boolean expression. In particular, it is possible to combine the not operator with the Groovy truth:
“not”操作符使用感叹号(!)表示,颠倒布尔表达式的值。特别的,可以将not操作符和the Groovy truth结合:
那么什么是the Groovy truth呢?
这是semantics中的内容。我认为the Groovy truth是决定一个表达式是true还是false的规则。你可以打开上面那个链接查看这些规则。关于semantics,后续会推出中文讲解!

assert (!true)    == false  //  true的否定是false      
assert (!'foo') == false // 'foo'不是一个空字符串,计算结果为true,再否定后返回false
assert (!'') == true //参照上一个注释

5.2. 三元运算符Ternary operator

三元运算符是一种简洁的写法,其等同于if/else
这个表达式:

if (string!=null && string.length()>0) {
result = 'Found'
} else {
result = 'Not found'
}

你可以使用三元运算符表达式代替:

result = (string!=null && string.length()>0) ? 'Found' : 'Not found'

应用Groovy truth中的规则,你还可以更简洁:

result = string ? 'Found' : 'Not found'

之所以可以这样写,是因为在Groovy truth的规则中:直接一个字符串为空则为false,不为空则为true。

5.3. Elvis操作符 Elvis operator

The “Elvis operator” is a shortening of the ternary operator. One instance of where this is handy is for returning a ‘sensible default’ value if an expression resolves to false-ish (as in Groovy truth). A simple example might look like this:
“Elvis”操作符是三元操作符的一种简写形式。如果一个表达式的(依据Groovy truth规则)为false,则将便捷的返回一个默认的合乎逻辑的结果。举个简单的例子:

displayName = user.name ? user.name : 'Anonymous'   
displayName = user.name ?: 'Anonymous'

这两个表达式的结果是等同的。而三元表达式显得有些臃肿。
这种写法的好处是优化了代码,减少了出错的机会。

6. 对象操作符 Object operators

6.1. Safe navigation operator

暂且翻译为:安全引用操作符。
这个操作符用于避免空指针异常的抛出。有个场景是,你再使用调用一个对象的方法或属性前先确认该对象是否为空。在空指针时,这个操作符运算后返回null来代替抛异常。例如:

def person = Person.find { it.id == 123 }    //返回一个Person对象person为null
def name = person?.name //使用这种操作符避免了空指针异常
assert name == null

6.2. 直接访问字段操作符 Direct field access operator

我们通常这样写代码,例如:

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'

对于初学者,说明一下:user.name等价于user.getName()而不是对字段的直接获取。对于Groovy而言,对字段默认生成getter,返回的是该字段的值。
你也可以通过字段直接访问操作符访问字段的值(经测试,该字段为私有也可以访问):

assert user.@name == 'Bob'    

6.2 方法指针操作符 Method pointer operator

方法指针操作符(.&)被用于储存一个方法的索引到变量中,以便于后续的调用:

def str = 'example of method reference'  //str变量包含一个字符串          
def fun = str.&toUpperCase//将str实例的toUpperCase方法作为索引储存在fun变量中
def upper = fun()//像普通方法一样调用。
assert upper == str.toUpperCase()

使用这种方法指针有诸多的好处。首先,这种类型的方法指针,是一种groovy.lang.Closure(闭包),所以闭包可以使用的任何地方都可以被方法指针代替。特别的,将现存的方法按照需求转化成指针方法也是合适的.

def transform(List elements, Closure action) {                    
def result = []
elements.each {
result << action(it)
}
result
}
String describe(Person p) {
"$p.name is $p.age"
}
def action = this.&describe
def list = [
new Person(name: 'Bob', age: 42),
new Person(name: 'Julia', age: 35)]
assert transform(list, action) == ['Bob is 42', 'Julia is 35']

①transform方法里面的集合的遍历(后续会有介绍)。这个遍历调用闭包,返回一个新的集合。
②我们定义一个函数传入一个Person对象返回一个字符串。
③定义describe的指针变量
注:transform(list, action)中的参数action是闭包,方法指针变量可以作为闭包使用,但是方法不可以作为闭包使用。
附一下Person类:

class Person{
public final String name;
public final int age;
public Person(Map map){
this.name=map.name;
this.age=map.age;
}
}

方法指针包含返回值和方法名。参数会在运行时进行解析,也就意味着,如果多个方法的方法名相同,语法结构不同,在运行时只有恰当的方法会被调用(和java中的就近元素相似):

def doSomething(String str) { str.toUpperCase() }    
def doSomething(Integer x) { 2*x }
def reference = this.&doSomething
assert reference('foo') == 'FOO'
assert reference(123) == 246

7. 正则表达式操作符 Regular expression operators

7.1 模式操作符Pattern operator

模式操作符(~)提供了一个简单的方式来创建java.util.regex.Pattern实例:

def p = ~/foo/
assert p instanceof Pattern

通常你会发现模式操作符是一个正斜杠字符串表达式,因为在Groovy中它可以是任意类型的字符串。

p = ~'foo'  //使用单引号字符串                                                      
p = ~"foo" //使用双引号字符串
p = ~$/dollar/slashy $ string/$ //使用美元-正斜杠字符串
p = ~"${pattern}" //可以使用GString

7.2.查找字符串 Find operator##

你可以直接使用查找操作符(=~)来创建一个java.util.regex.Matcher实例:

def text = "some text to match"
def m = text =~ /match/
assert m instanceof Matcher
if (!m) {
throw new RuntimeException("Oops, text not found!")
}
  • =~ creates a matcher against the text variable, using the pattern on the right hand side
  • the return type of =~ is a Matcher
  • equivalent to calling if (!m.find())

Since a Matcher coerces to a boolean by calling its find method, the =~ operator is consistent with the simple use of Perl’s =~ operator, when it appears as a predicate (in if, while, etc.).

7.3 匹配操作符 Match operator

匹配操作符(==~)是查找操作符的一个变形,返回的是boolean而不是Matcher,对输入的字符串要求严格的匹配:

m = text ==~ /match/ //==~使用正则表达式匹配这个subject,但是匹配规则是严格的                                             
assert m instanceof Boolean //返回值是一个boolean值
if (m) { //等同于调用if (text ==~ /match/)
throw new RuntimeException("Should not reach that point!")
}

8. 其他操作符 Other operators

8.1 展开操作符 Spread operator

展开操作符(*.)通常被集合对象调用其所有的对象使用。等价于遍历调用,并将结果收集在一个集合中:

class Car {
String make
String model
}
def cars = [
new Car(make: 'Peugeot', model: '508'),
new Car(make: 'Renault', model: 'Clio')]
def makes = cars*.make //返回一个只包含make条目的集合
assert makes == ['Peugeot', 'Renault']

展开操作符是一个指针安全操作符,即即使返回值是一个空指针也不会抛出NullPointerException而代之返回null

cars = [
new Car(make: 'Peugeot', model: '508'),
null,
new Car(make: 'Renault', model: 'Clio')]
assert cars*.make == ['Peugeot', null, 'Renault']
assert null*.make == null

展开操作符可以被用在任意一个实现了Iterable接口的类中:

class Component {
Long id
String name
}
class CompositeObject implements Iterable<Component> {
def components = [
new Component(id: 1, name: 'Foo'),
new Component(id: 2, name: 'Bar')]

@Override
Iterator<Component> iterator() {
components.iterator()
}
}
def composite = new CompositeObject()
assert composite*.id == [1,2]
assert composite*.name == ['Foo','Bar']

8.1.1.展开方法参数 Spreading method arguments

有这样一种情况,即方法中的参数个数如果等同于一个集合,在这种情况下,你可以使用展开操作符作为参数被这个方法调用。例如:

int function(int x, int y, int z) {
x*y+z
}

定义一个集合:

def args = [4,5,6]

你可以这样直接调用这个方法:

 assert function(*args) == 26

如果这个集合只有一个元素,你也可以这样调用:

args = [4]
assert function(*args,5,6) == 26

8.1.2. 展开集合元素Spread list elements

不再赘述,直接上代码:

def items = [4,5]                      
def list = [1,2,3,*items,6]
assert list == [1,2,3,4,5,6]

8.1.3.展开map集合 Spread map elements

不再赘述,直接上代码,需要注意的是不同于集合,这里使用的是(*:)

def m1 = [c:3, d:4]                   
def map = [a:1, b:2, *:m1]
assert map == [a:1, b:2, c:3, d:4]

8.2 区间操作符 Range operator

Groovy支持区间的概念,提供了符号(..)来生成区间对象:

def range = 0..5                                    
assert (0..5).collect() == [0, 1, 2, 3, 4, 5]
assert (0..<5).collect() == [0, 1, 2, 3, 4]
assert (0..5) instanceof List //groovy.lang.Range实现了List接口
assert (0..5).size() == 6

你可以创建任意的Comparable对象并具有next()和previous()方法。例如:

assert ('a'..'d').collect() == ['a','b','c','d']

8.3 比较操作符 Spaceship operator

比较操作符(<=>)代表了compareTo方法:

assert (1 <=> 1) == 0
assert (1 <=> 2) == -1
assert (2 <=> 1) == 1
assert ('a' <=> 'z') == -1

8.4.下标操作符 Subscript operator

不好解释,直接上代码:可用于替代方面:getAt()和putAt();

def list = [0,1,2,3,4]
assert list[2] == 2 //可以被getAt(2)代替使用
list[2] = 4 //putAt
assert list[0..2] == [0,1,4]
list[0..2] = [6,6,6]
assert list == [6,6,6,3,4]

下标操作符,是一个简便的方式代替自定义的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")
}
}
def user = new User(id: 1, name: 'Alex')
assert user[0] == 1
assert user[1] == 'Alex'
user[1] = 'Bob'
assert user.name == 'Bob'

8.5.in操作符 Membership operator

in操作符等价于isCase方法。在一个集合中,它等价于调用contains,例如:

def list = ['Grace','Rob','Emmy']
assert ('Emmy' in list)

等价于调用:list.contains(‘Emmy’)或者list.isCase(‘Emmy’)

8.6. 身份识别操作符Identity operator

在Groovy中,使用(==)等同于Java中的这个操作符。在Groovy中,也可以调用equals。如果你想要比较两个索引,你应该使用is比如下面的例子:
def list1 = [‘Groovy 1.8’,’Groovy 2.0’,’Groovy 2.3’]
def list2 = [‘Groovy 1.8’,’Groovy 2.0’,’Groovy 2.3’]
assert list1 == list2
assert !list1.is(list2)

8.7.强制操作符 Coercion operator

强制操作符(as)用于变量的强制转换。强制转换操作符转换对象从一个类型向另外一个类型而不考虑其兼容性。举个例子:

Integer x = 123
String s = (String) x

由于Integer不可以自动转化成String,所以在运行时会抛出一个ClassCastException 。
不过可以使用强制操作符替代:

Integer x = 123
String s = x as String

在强转一个对象到另一种类型是,除非目标类型是同一种源码类型,强转才会返回一个新的对象。自定义的强转规则如果实现了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 coerced into $target")
}
}
def u = new User(name: 'Xavier')
def p = u as Identifiable
assert p instanceof Identifiable
assert !(p instanceof User)

8.8.钻石运算符 Diamond operator

钻石运算符在java中也有出现在java7中,具有自动推断的能力。
List<String> strings = new LinkedList<>()

8.9. 调用操作符Call operator

调用操作符()被用来默认调用名为call的方法。定义了call方法的对象,你可以使用调用操作符来取代.call
举个例子:

class MyCallable {
int call(int x) {
2*x
}
}

def mc = new MyCallable()
assert mc.call(2) == 4
assert mc(2) == 4

9. 操作符优先级Operator precedence

下标按优先级列出了Groovy所有的操作符

Level Operator(s) Name(s)
1 new () object creation, explicit parentheses
() {} [] method call, closure, literal list/map
. .& .@ member access, method closure, field/attribute access
?. * . : safe dereferencing, spread, spread-dot, spread-map
! (type) bitwise negate/pattern, not, typecast
[] ++ – list/map/array index, post inc/decrement
2 ** power
3 ++ – + - pre inc/decrement, unary plus, unary minus
4
    / %
multiply, div, modulo
5
    -
addition, subtraction
6 << >> >>> .. ..< left/right (unsigned) shift, inclusive/exclusive range
7 < <= > >= in instanceof as less/greater than/or equal, in, instanceof, type coercion
8 == != <=> equals, not equals, compare to
=~ ==~ regex find, regex match
9 & binary/bitwise and
10 ^ binary/bitwise xor
11 位或 binary/bitwise or
12 && logical and
13 双或 logical or
14 ? : ternary conditional
?: elvis operator
15 = *= = /= %= += -= <<= >>= >>>= &= ^= various assignments

10. Operator overloading

Groovy允许重载各种操作符所以你可以将这些应用于你自己的类。考虑以这个简单的类:

class Bucket {
int size

Bucket(int size) { this.size = size }

Bucket plus(Bucket other) {
return new Bucket(this.size + other.size)
}
}

注意:Bucket实现了特殊的方法被称作plus();
仅仅是实现了plus()方法,这个Bucket类现在就允许使用+操作符像这样:

def b1 = new Bucket(4)
def b2 = new Bucket(11)
assert (b1 + b2).size == 15

所有(不含比较器)的Groovy操作符都有相应的方法你可以在你自己的类中予以实现。仅仅要求你的这些方法是public,名称正确,参数数量正确。参数类型,取决于你想要支持的类型。例如,你可以支持以下声明:

assert (b1+11).size == 15

通过实现plus()方法:

Bucket plus(int capacity) {
return new Bucket(this.size + capacity)
}

下面是一个操作符于相应方法的表格:

Operator Method Operator Method
+ a.plus(b) a[b] a.getAt(b)
- a.minus(b) a[b] = c a.putAt(b, c)
* a.multiply(b) a in b b.isCase(a)
/ a.div(b) << a.leftShift(b)
% a.mod(b) ‘>>’ a.rightShift(b)
** a.power(b) ‘>>>’ a.rightShiftUnsigned(b)
a.or(b) ++ a.next()
& a.and(b) a.previous()
^ a.xor(b) +a a.positive()
as a.asType(b) -a a.negative()
a() a.call() ~a a.bitwiseNegative()

Groovy的操作符告一段路……..

2016-1-11 14:05;