Groovy(七)-其他运算符

译文: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<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']

展开的方法参数
有一些情况一个集合正好跟方法调用的参数匹配,在这个情况下,我们就可以使用展开操作符进行调用方法,例如,你想象一下你以下的方法签名:

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

下标操作符
下标操作符是getAtputAt的简写标识,这取决于你是在一个赋值符号的左边还是右边来查找:

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<String> 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)
}

如下表就是操作符以及与之对应的方法:


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,047评论 9 119
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,482评论 6 13
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,292评论 18 399
  • 大家中午好 小海豹为大家带来每日午豹 1、摩拜又搞大新闻!推出人工智...
    玫瑰钻石婚恋阅读 174评论 0 0
  • 一毛不拔 知乎喝茶 早春新叶 掐掐掐掐
    一元真人阅读 111评论 0 3