Kotlin 替换非空断言的几种方式

Kotlin 出现断言的两种情形

  1. IDE java 与 kotlin 自动转换时,自动添加非空断言的代码
  2. Smart Cast 失效

代码展示:


class JavaConvertExample {
    private var name: String? = null
    fun init() {
        name = ""
    }

    fun foo() {
        name = null;
    }

    fun test() {
        if (name != null) {
// 这里去掉 !! 编译器会报错
//                        非空断言
//                           ↓
            val count = name!!.length
        }
    }
}

也许疑问,为什么判断非空后,还需要断言,因为比如出现如下情况


class JavaConvertExample {
    private var name: String? = null
    fun init() {
        name = ""
    }

    fun foo() {
        name = null;
    }

    fun test() {
        if (name != null) {
            // 几百行代码
            foo()
            //几百行代码
            val count = name!!.length
        }
    }
}

逻辑变复杂后,他可能就仍然会变为空。所以,进入正题,避免使用非空断言的几种方式

第一种,避免直接访问变量或者全局变量,将其改为传参的形式:

//    改为函数参数
//        ↓
fun test(name: String?) {
    if (name != null) {
//             函数参数支持Smart Cast
//                      ↓
        val count = name.length
    }
}

在 kotlin 当中,函数的参数是不可变的,因此,当我们将外部的成员变量或者全局变量以函数参数的形式传进来以后,它可以用于 Smart Cast 了。

第二种,避免使用可变变量 var,改为 val 不可变变量:

class JavaConvertExample {
//       不可变变量
//           ↓
    private val name: String? = null

    fun test() {
        if (name != null) {
//               不可变变量支持Smart Cast
//                          ↓
            val count = name.length
        }
    }
} 

这种方式很好理解,既然引发问题的根本原因是可变性导致的,我们直接将其改为不可变的即可。

第三种,借助临时的不可变变量:


class JavaConvertExample {
    private var name: String? = null

    fun test() {
//        不可变变量
//            ↓
        val _name = name
        if (_name != null) {
            // 在if当中,只使用_name这个临时变量
            val count = _name.length
        }
    }
}

第四种,借助 kotlin 提供的 标准函数 let:


class JavaConvertExample {
    private var name: String? = null

    fun test() {
//                      标准函数
//                         ↓
        val count = name?.let { it.length }
    }
}

第五种,借助 kotlin 提供的 lateinit 关键字

class JavaConvertExample {
//         稍后初始化             不可空
//            ↓                   ↓
    private lateinit var name: String

    fun init() {
        name = "Tom"
    }

    fun test() {
        if (this::name.isInitialized) {
            val count = name.length
        } else {
            println("Please call init() first!")
        }
    }
}

fun main() {
    val example = JavaConvertExample()
    example.init()
    example.test()
}

第六种,使用 by lazy 委托:


class JavaConvertExample {
//         不可变        非空   懒加载委托
//           ↓           ↓        ↓
    private val name: String by lazy { init() }
    
    fun init() = "Tom"
    
    fun test() {
        val count = name.length
    }
}

可以看到,我们将 name 这个变量改为了不可变的非空属性,并且,借助 Kotlin 的懒加载委托来完成初始化。借助这种方式,我们可以尽可能地延迟初始化,同时,也消灭了可变性、可空性。

以上几种方式,可以避免我们使用非空断言

注:摘自《朱涛 · Kotlin编程第一课》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值