Kotlin 中的判空操做 Elvis 操做符使用踩坑

Kotlin 做为一门有着所谓空安全特性的(年轻)编程语言,有时出于实际业务场景须要仍是会把变量声明成可空(Null-able)的,好在因为空安全特性,编译器会强制咱们对可空变量进行判空检查(除非你使用了非空断言 !! 强制让编译器闭嘴)。Kotlin 以彻底兼容 Java 为设计原则,设计者们该是在设计阶段就预见到了使用者们可能仍是须要进行不少判空检查,因而引入了很是简洁优雅的 Elvis 操做符,请看代码:html

Java 典型判空操做:java

/** * <p>控制台打印字符串的长度是多少</> * @param str:给定的字符串 */
    public void askStringLength(String str){
        int i;
        //Java 中咱们通常这样判空,或者使用那个三目运算符
        if (str == null){
            i = 0;
        } else{
            i = str.length();
        }

        //或者使用三目运算符
        i = str == null? 0 : str.length();

        System.out.println("The length is " + i);
    }
复制代码

如上,Java 没有所谓空安全特性。你可能须要写大量上面那样的判空模板代码,并且你确定没法在每个变量可能为空的地方加上这种防护性代码,毕竟项目里很容易积攒到成千上万的变量,给这么多变量写上防护性代码想一想都头大!固然对于很大几率会抛出空指针异常的地方编译器会作高亮提示,但会给出提示的地方放之整个项目而言实在微不足道。咱们手动 new 出来的对象和 null,做为有着彻底不一样行为的两种实体竟然能够赋值给同一个变量,如此,若是在该判空的地方没作判空,就是一个在运行时可能会抛出 NPE(NullPointerException) 的bug!编程

Kotlin 从新设计了一套彻底不一样于 Java 的类型系统,声称可大大较少项目中的 NullPointerExceptio(Kotlin因以彻底兼容 Java 为设计原则,于是没法彻底杜绝 NPE)。就我本人经验看来,效果还不赖,使用 Koltin 项目中的 NPE 确实少多了。并且 Koltin 中对可能抛出 NPE 的地方作判空检查的方式比之 Java 而言实在优雅,今后远离那些烦人的 if-else,只需借助所谓 Elvis 操做符。安全

关于 Kotlin 的类型系统与 Java 之异同非本文重点,重点是在 Kotlin 中,若是一个变量是可空的,你在访问此变量时若是不作判空检查是通不过编译的(除非你使用了非空断言 !! 强制让编译器闭嘴)!如此使得咱们的代码在运行时抛出 NPE 的几率大大减小!嗯能在编译阶段发现的问题就别留到运行时再发现!下面咱们直接看看上面包含判空操做的 Java 代码其等价的 Koltin 代码是怎么样的。编程语言

Kotlin 典型判空操做:ui

/** * * <p>控制台打印字符串的长度是多少</> * @param str:给定的字符串 */
    fun askStringLength(str: String?) {
        val i: Int
        // Kotlin 中咱们通常这样对变量判空
        // ?. 是 Kotlin 中所谓的安全调用操做符
        // 若是 str 非空,就返回 str.length,不然返回 null,表达式(str?.length)返回的类型是 Int? 嗯有可能为 null
        // ?; 就是所谓的 Elvis 操做符
        // 若是 ?: 左侧表达式非空,elvis 操做符就返回其左侧表达式,不然返回右侧表达式
        i = str?.length?:0//str.length 这么写是通不过编译的
        // 一路流式操做下来是否是感受很顺畅,那可比 if-else 舒服多了!注意 Kotlin 中的 Elvis 操做符不是三目操做符,Kotlin 中是不存在三木操做符的

        println("The length is $i")
    }
复制代码

Kotlin 中咱们通常这样对变量判空:spa

i = str?.length?:0
复制代码

?. 是 Kotlin 中所谓的安全调用操做符,若是 str 非空,就返回 str.length,不然返回 null,表达式(str?.length)返回的类型是 Int? , 嗯有可能为 null,?; 就是所谓的 Elvis 操做符,若是 ?: 左侧表达式非空,elvis 操做符就返回其左侧表达式,不然返回右侧表达式(作默认值)。.net

此时若是咱们有了新需求,要求计算一个 List 中全部字符串的长度之和,你极可能会这么写:设计

/** *<p>控制台打印列表中全部字符串长度之和</> * @param strings:给定的字符串列表 */
    fun askStringLength(strings: List<String?>) {
        var i: Int = 0
        strings.forEach { str ->
            i = str?.length?:0 + i //注意这行代码,固然你更可能写成 i += str?.length?:0,为了说明问题先按我写的来
        }
        println("The length is $i")
    }
复制代码

若是真像上面那样写了,那你确定得不到正确的结果!你能够运行试试!指针

缘由在于 Elvis 操做符的优先级较低,请看下方我从 Kotlin 官网扒来的操做符优先级表:

Precedence Title Symbols
Highest Postfix ++, --, ., ?., ?
Prefix -, +, ++, --, !, label
Type RHS :, as, as?
Multiplicative *, /, %
Additive +, -
Range ..
Infix function simpleIdentifier
Elvis ?:
Named checks in, !in, is, !is
Comparison <, >, <=, >=
Equality ==, !==
Conjunction &&
Disjunction ||
Lowest Assignment =, +=, -=, *=, /=, %=

注意 Elvis 操做符的位置!

这行代码:

i = str?.length?:0 + i
复制代码

其实等价于

i = str?.length ?: (0 + i)
复制代码

因此想让代码正确运行咱们其实应该写成这样:

i = (str?.length?:0) + i
复制代码

使用时千万要注意操做符的优先级!

完。

相关文章
相关标签/搜索