Kotlin之Elvis 操作符

Elvis 操作符的优先级

首先要注意到,Elvis 操作符的优先级是比较低的,特别是比加减乘除的运算符低,混合运算时一定要记得加上括号。比如:

fun <T> Iterable<T>.contentHashCode(): Int {
    return fold(1) {
        hash, element ->
        hash * 31 + (element?.hashCode() ?: 0)
    }
}

这里如果不用括号将 element?.hashCode() ?: 0 括起来,编译器就会认为这句表达式是 (hash * 31 + element?.hashCode()) ?: 0,出现编译错误。

与 ?. 配合使用时的问题

Elvis 操作符与安全调用符 ?. 配合使用时,一定要考虑到安全调用符前后是否为空,否则就会带来流程控制混乱的问题。对于任何一个下列的表达式:

val v = a?.b ?: c

因为 ?. 的优先级比 ?: 高,首先计算 a?.b,按照安全调用符的规则,如果 a == null 则结果为 null,执行 c,但如果 a.b == null,也会执行 c。也就是说,它的执行逻辑是这样的:

var temp = if(a != null) a.b else null
val v = if(temp != null) temp else c

它等价于:

val v = if(a == null || a.b == null) c else a.b

实际使用时一定要注意 ?. 前后是否都可能为 null。

?: 与流程控制语句的搭配使用

我发了一个 Kotlin 写的前序遍历二叉树的 Gist,地址在这里:PreOrderTraversing.kt,整个项目:DataStructureLearning-Kotlin 非递归遍历二叉树的代码如下:

private fun preOrderLoop(
    root: Node? = this.root,
    list: MutableList<Int> = mutableListOf()
): List<Int> {
    val stack = ArrayDeque<Node>()
    stack.push(root ?: return list)

    while (stack.isNotEmpty()) {
        val nodeNow = stack.pop()!!
        list += nodeNow.value
        nodeNow.right?.let { stack.push(it) }
        nodeNow.left?.let { stack.push(it) }
    }
    return list
}

第二句很有意思。ArrayDeque 不能容纳 null,一旦插入 null 就会抛出 NPE,而我们的函数要求 root 为 null 时返回一个空的 List,所以这里 push() 的参数写成 root ?: return list,这句代码的逻辑如下:

if(root == null) return list
stack.push(root)

此外,Elvis 还可以配合 break 和 continue 来控制循环流程。

改良 Elvis

Elvis 操作符很方便,但只能连接表达式,我们可以写一个扩展函数来作为加强版的 Elvis 操作符。

inline infix fun <T : Any> T?.ifNull(block: (T?) -> T): T {
    if (this == null) {
        return block(this)
    }
    return this
}

使用方式:

val file = java.io.File("C:\\FakeFile")
val parent = file.parent ifNull {
    // do something here
}    

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏二进制文集

Java 回调机制

Teacher 中,有一个解决问题的对象:Student,在Student中解决问题之后,再通过引用调用Teacher中的tellAnswer接口,所以叫回调。

5463
来自专栏一个会写诗的程序员的博客

《Kotin 编程思想·实战》

Xtend是Eclipse推出的一个新的JVM语言,并无意替代Java,而是以己之长补Java之短,精简代码,无类型,改进可读和维护。Eclipse Xtend...

1343
来自专栏积累沉淀

Java设计模式(二十三)----解释器模式

解释器模式 定义:解释器模式是类的行为模式。给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个...

2045
来自专栏Ryan Miao

Java8学习(4)-Stream流

Stream和Collection的区别是什么 流和集合的区别是什么? 粗略地说, 集合和流之间的差异就在于什么时候进行计算。集合是一个内存中的数据结构,它包...

9837
来自专栏函数式编程语言及工具

Scalaz(19)- Monad: \/ - Monad 版本的 Either

  scala标准库提供了一个Either类型,它可以说是Option的升级版。与Option相同,Either也有两种状态:Left和Right,分别对应Op...

2915
来自专栏晓晨的专栏

C#自动识别文件编码

2533
来自专栏IT笔记

2016头条校招笔试题(LRU)算法之JAVA实现

操作系统中可以使用LRU(Least Recently Used)内存淘汰旧数据的策略,如果内存需要加载新数据但空间不足,则会按照最近访问时间进行排序,并将最老...

34010
来自专栏测试开发架构之路

C语言程序设计50例(三)(经典收藏)

【程序31】 题目:请输入星期几的第一个字母来判断一下是星期几,如果第一个字母一样,则继续    判断第二个字母。 1.程序分析:用情况语句比较好,如果第一个字...

1.2K9
来自专栏DT乱“码”

ClassPathXmlApplicationContext方式读取配置文件

public interface BeanFactory {   public Object getBean(String id); }   //实现类Clas...

2095
来自专栏封碎

Android系统事件的recycle原理 博客分类: Android Android

    最近封装一些功能性的jar包,因为需要产生一些动作,然后给调用者一些回调,所以用到了事件和监听器。     举个例子,比如DragListene...

1392

扫码关注云+社区

领取腾讯云代金券