译文:Groovy Language Documentation
主目录见:Android高级进阶知识(这是总目录索引)
因为前段时间比较忙,所以[Android高级进阶]一直没更新,接下来有时间会重新更新,下一篇将会是热更新框架AndFix的完全解析,敬请期待...
今天算是运算符的最后一篇了,运算符的篇幅还真是很长,所以大家可想而知他的重要性!!!!
展开(spread)操作符
展开操作符(*.)会触发操作集合中的所有对象。相当于调用了遍历集合和合并结果到一个集合的操作。
class Car {
String make
String model
}
def cars = [
new Car(make: 'Peugeot', model: '508'),
new Car(make: 'Renault', model: 'Clio')] 1
def makes = cars*.make 2
assert makes == ['Peugeot', 'Renault'] 3
1.创建一个car对象的集合,这个list是一个集合对象。
2.调用展开操作符,访问每个对象的make属性。
3.返回make属性组成的集合和String集合的匹配
展开操作符是空安全的,这意味着如果集合中的元素是空的话,那么他就会返回null来代替抛出NullPointerException
异常。
cars = [
new Car(make: 'Peugeot', model: '508'),
null, 1
new Car(make: 'Renault', model: 'Clio')]
assert cars*.make == ['Peugeot', null, 'Renault'] 2
assert null*.make == null 3
1.建一个带有null元素的集合
2.使用展开操作符不会返回NullPointerException
异常
3.如果接收者是null
的,那么这种情况返回值就会是null
展开操作符可以用在任何实现了Iterable
接口的类中:
class Component {
Long id
String name
}
class CompositeObject implements Iterable {
def components = [
new Component(id: 1, name: 'Foo'),
new Component(id: 2, name: 'Bar')]
@Override
Iterator iterator() {
components.iterator()
}
}
def composite = new CompositeObject()
assert composite*.id == [1,2]
assert composite*.name == ['Foo','Bar']
展开的方法参数
有一些情况一个集合正好跟方法调用的参数匹配,在这个情况下,我们就可以使用展开操作符进行调用方法,例如,你想象一下你以下的方法签名:
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
展开的集合元素
当被用在一个集合常量中,展开操作符就可以将集合元素内容内联到集合中:
def items = [4,5] 1
def list = [1,2,3,*items,6] 2
assert list == [1,2,3,4,5,6] 3
1.items
是一个集合
2.我们想把items
集合直接插入list
中而无需调用addAll()
方法
3.items
集合的内容已经内联到list
中了
展开的Map元素
展开Map元素操作符和展开List元素操作符的使用方式是一样的,但是对于Map来说,他允许你像下面这种方式来内联Map:
def m1 = [c:3, d:4] 1
def map = [a:1, b:2, *:m1] 2
assert map == [a:1, b:2, c:3, d:4] 3
1.m1
是我们要内联的元素
2.我们使用*:m1
标记法来将m1
内联进map中
3.map
包含了所有m1
的元素
展开Map操作符的位置是有关联的,就像下面的例子说明:
def m1 = [c:3, d:4] 1
def map = [a:1, b:2, *:m1, d: 8] 2
assert map == [a:1, b:2, c:3, d:8] 3
1.m1
是我们要内联的元素
2.我们使用*:m1
操作符来展开m1
的内容进map中,但是在展开d
的时候重新定义了
3.map
包含了所有的可能的键,但是d
被重定义了
范围操作符
Groovy支持范围的概念而且提供了..
来创建元素范围:
def range = 0..5 1
assert (0..5).collect() == [0, 1, 2, 3, 4, 5] 2
assert (0..<5).collect() == [0, 1, 2, 3, 4] 3
assert (0..5) instanceof List 4
assert (0..5).size() == 6 5
1.一个简单的integer类型范围,并且赋值给局部变量
2.一个IntRange
包含了边界
3.一个IntRange
不包含上边界
4.groovy.lang.Range
实现了List
5.这意味着你可以使用size
方法
Ranges
的实现是轻量级的,只有下边界和上边界被保存,你可以从任何包含next()
方法和previous()
方法的Comparable
对象来创建范围中的next / previous项,例如,你可以用下面这种方法来创建范围:
assert ('a'..'d').collect() == ['a','b','c','d']
<=>
操作符
<=>
代理了compareTo
方法:
assert (1 <=> 1) == 0
assert (1 <=> 2) == -1
assert (2 <=> 1) == 1
assert ('a' <=> 'z') == -1
下标操作符
下标操作符是getAt
和putAt
的简写标识,这取决于你是在一个赋值符号的左边还是右边来查找:
def list = [0,1,2,3,4]
assert list[2] == 2 1
list[2] = 4 2
assert list[0..2] == [0,1,4] 3
list[0..2] = [6,6,6] 4
assert list == [6,6,6,3,4] 5
1.[2]
可以用来替换getAt(2)
2.如果在赋值符的左边,将会调用putAt
3.getAt
也支持范围
4.做putAt
操作
5.list
变化了
下标操作符是为了解构对象的一种getAt/putAt
操作的自定义实现的一种简便方式。
class User {
Long id
String name
def getAt(int i) { 1
switch (i) {
case 0: return id
case 1: return name
}
throw new IllegalArgumentException("No such element $i")
}
void putAt(int i, def value) { 2
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') 3
assert user[0] == 1 4
assert user[1] == 'Alex' 5
user[1] = 'Bob' 6
assert user.name == 'Bob' 7
1.User
类定义了getAt
的自定义实现
2.User
类定义了putAt
的自定义实现
3.创建一个user
实例
4.使用下标0则会取回用户的id
5.使用下标1则会取回用户的name
6.通过putAt
的代理,我们可以通过下标来写属性
7.确定name是否真的被改变了
成员操作符
成员操作符in
相当于调用了inCase
方法,在List
上下文中相当于调用了contains
方法,例如下面的例子:
def list = ['Grace','Rob','Emmy']
assert ('Emmy' in list) 1
1.相当于调用了list.contains('Emmy')
和list.isCase('Emmy')
恒等操作符
在Groovy中,使用==
和在java中使用是不一样的,在Groovy中是相当于调用equals
,如果你要比较对象的引用,那么你要调用is
:
def list1 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3'] 1
def list2 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3'] 2
assert list1 == list2 3
assert !list1.is(list2) 4
1.创建一个元素是String的list
2.创建一个跟list1相同的list
3.使用==
来比较两个list
4.但是使用is
,我们会发现两个对象是不一样的
强转操作符
强转操作符as
是转换的一种变型,强转操作符转换一个对象使其不需要在赋值的时候强制转换,例如:
Integer x = 123
String s = (String) x 1
1.Integer
不会被强转成String
,所以在运行时会报ClassCastException
这个可以使用强转操作符来解决:
Integer x = 123
String s = x as String 1
1.Integer
不会被强转成String
,但是用as
则可以
当一个对象强制转化为另一种类型,除非转化的类型是相同的,不然会创建一个新的对象,强转的规则不同取决于转化的类型的不同,如果常规的规则没有找到则会强转失败,自定义转换规则可以实现asType
方法:
class Identifiable {
String name
}
class User {
Long id
String name
def asType(Class target) { 1
if (target == Identifiable) {
return new Identifiable(name: name)
}
throw new ClassCastException("User cannot be coerced into $target")
}
}
def u = new User(name: 'Xavier') 2
def p = u as Identifiable 3
assert p instanceof Identifiable 4
assert !(p instanceof User) 5
1.User
定义了一个自定义转换规则来将User
转换为Identifiable
2.创建一个User
实例
3.使用强转操作符将User
转换为Identifiable
4.目标是一个Identifiable
实例
5.目标已经不是User
实例了
菱形操作符
菱形操作符是一个语法糖,是为了兼容java 7中的菱形操作符。他被用于在声明期间声明泛型:
List strings = new LinkedList<>()
在动态Groovy中,这完全没有用,在Groovy的静态检查中,这也是可选的
调用操作符
调用操作符()
是调用了call
方法,对于任何定义了call
方法的类,你可以省略.call
部分而使用调用操作符:
class MyCallable {
int call(int x) { 1
2*x
}
}
def mc = new MyCallable()
assert mc.call(2) == 4 2
assert mc(2) == 4 3
1.MyCallable
定义了一个方法名字call
,注意他不需要实现java.util.concurrent.Callable
2.我们能通过传统的call
方法来调用方法
3.由于call
操作符我们可以省略掉.call
操作符优先级
下表按照操作符的优先级列出了groovy中所有的运算符:
运算符重载
Groovy允许你重载不同的操作符而使他们可以在你的程序里面使用,例如:
class Bucket {
int size
Bucket(int size) { this.size = size }
Bucket plus(Bucket other) { 1
return new Bucket(this.size + other.size)
}
}
1.Bucket
实现了一个特殊的操作符plus()
因为通过重载了plus()
,所以Bucket
可以使用+
在使用中:
def b1 = new Bucket(4)
def b2 = new Bucket(11)
assert (b1 + b2).size == 15 1
1.两个Bucket
可以被+
操作符相加在一起
所有的操作符都有一个对应的方法,你可以在自己的程序中实现。唯一要注意的是方法必须为public,名字是对的,参数是一致的。参数的类型取决于你想要支持什么类型的参数在操作符的右边,例如:
assert (b1 + 11).size == 15
通过如下实现plus()
:
Bucket plus(int capacity) {
return new Bucket(this.size + capacity)
}
如下表就是操作符以及与之对应的方法: