Kotlin 可空类型系统的学习

引言

什么是类型?为啥变量拥有类型?

“类型就是数据的分类… 决定了该类型可能的值和可以完成的操作。”

那么套用这个定义来分析一下NULL值在类型系统的地位。

比如在java中null是一种数值,但是当一个引用为null的时候,并不可以在这个引用调用该类型的任何方法,这不就意味着java的类型系统在值为NULL的时候不能很好的工作,
甚至当你使用instanceof运算符的时候,它的类型都是NULL。

既然如此,为什么不把null当做一种类型呢?

所以,Kotlin就将NULL值当做一个类型来支持。这样带来的优点就是,将NullPointerException异常从一个运行时候可能出现的错误,变成一个在编译时候就会暴露的错误。

在性能方面,值得一提的是,Kotlin中的空类型并不是非空类型的包装。所有的检查都发生在编译器。也就意味着不会在运行时带来额外的开销。

Null类型可执行的操作

最重要的就是进行类型转换。
也就是通过 !=null 来将null类型转换成非空类型。

当然可以通过定义扩展函数来对null类型添加可调用的方法。

例如

fun String?.verifyNull() {
   
    if (this == null) {
   
        println("input is null")
    } else {
   
        println("input is $this")
    }
}

fun main() {
   
    var input : String?

    input = null
    input.verifyNull()

    input = "abc"
    input.verifyNull()
}

我们就为String 添加了一个对于null值的调用。

回到java,在java中,this一定是非空的,毕竟我们是无法在一个空引用上调用方法的,因为java方法的调用是通过实例进行分发的。
但是在Kotlin中,this可以是Null的,毕竟扩展函数其实是将引用当做一个参数传入了函数中,参数可以为空,所以this自然也可以为空。

安全调用运算符?.

让我们来进行第一步的简化。
if (this != null) { ... }
如果为了支持空类型来引入这么多多余的代码有点没必要,
那么不如用一个符号来代替上述的含义。
?. 意味着 把一次null检查和一次方法调用合并在成一个操作。
即 如果这个值不为空就调用,为空就不调用并返回null。
当然不只是调用方法,还可以访问属性,毕竟访问属性也是方法调用,调用get()嘛。

=> 进一步来说,因为?.是有返回值的,所以在你的对象中拥有的可空的属性也拥有可空属性的时候就可以进行链式多个安全调用。
A ?. a ?. b ?. c()

Elvis 运算符 ?:

又叫做null合并运算符。
它的接受两个运算符,当第一个运算数为Null,运算结果就是第二个运算符;当第一个运算符不为Null,运算结果就是第一个运算符。

一般讲?:放在?.调用的结尾,来做安全调用的收尾工作。

例如

fun strLenSafe(s : String?) : Int = s ?. length ?: 0

帅气一点

fun String?.strLenSafe() : Int = this ?. length ?: 0

不过两种本质上是一样的。

加上 returnthrow 这样的操作是表达式,所以也可以写在 ?: 的右边。

安全类型转换 as?

不见as直接进行类型转换,如果失败就会抛异常。
加上as? 失败就返回null,从而使用?: 来收尾。

例如

override fun equals(o: Any?): Boolean {
   
       val other = o as? T ?: return false 
       return true;
}

let 函数

当你要给一个接受非空参数的函数传递可空参数时,推荐使用let
let 做的事情其实的把调用它的对象变成了lambda表达式的参数。

从而像这样调用 ?. let { xx(it) }

当然如果要检查多个值是否为null时,还是使用普通的 if 表达式来一次性检查所有的值更简单一些。

非空断言 !!

!! 就是在对编译器咆哮,这个值一定不为空。。
当然如果为空了,就会在你定义 !! 的地方抛出异常。

在一种情况下,比如当你在一个函数中检查了某个值不为null,而在另一个函数中使用这个值,你可以通过逻辑保证这个值一定不为null,但是编译器对这种情况就无能为力了,所以可以使用!!。

延迟初始化 lateinit

很多框架会在你的一个对象实例创建后,用专门的方法来初始化对象。
在这种情况下,如果存在一个可空的属性,那么逻辑上你可以保证,每次使用该属性都是非空的,那么你就可以使用lateinit来标记这个可空属性,避免频繁使用!!或者非空判断。

类型参数可空性

即使不是用?来结尾,参数也有可能是可空的。

泛型,毕竟可空类型也是泛型的一种,如果想避免这个问题,就指定一个非空上界吧。

全部评论

相关推荐

05-11 11:48
河南大学 Java
程序员牛肉:我是26届的双非。目前有两段实习经历,大三上去的美团,现在来字节了,做的是国际电商的营销业务。希望我的经历对你有用。 1.好好做你的CSDN,最好是直接转微信公众号。因为这本质上是一个很好的展示自己技术热情的证据。我当时也是烂大街项目(网盘+鱼皮的一个项目)+零实习去面试美团,但是当时我的CSDN阅读量超百万,微信公众号阅读量40万。面试的时候面试官就告诉我说觉得我对技术挺有激情的。可以看看我主页的美团面试面经。 因此花点时间好好做这个知识分享,最好是单拉出来搞一个板块。各大公司都极其看中知识落地的能力。 可以看看我的简历对于博客的描述。这个帖子里面有:https://www.nowcoder.com/discuss/745348200596324352?sourceSSR=users 2.实习经历有一些东西删除了,目前看来你的产出其实很少。有些内容其实很扯淡,最好不要保留。有一些点你可能觉得很牛逼,但是面试官眼里是减分的。 你还能负责数据库表的设计?这个公司得垃圾成啥样子,才能让一个实习生介入数据库表的设计,不要写这种东西。 一个公司的财务审批系统应该是很稳定的吧?为什么你去了才有RBAC权限设计?那这个公司之前是怎么处理权限分离的?这些东西看着都有点扯淡了。 还有就是使用Redis实现轻量级的消息队列?那为什么这一块不使用专业的MQ呢?为什么要使用redis,这些一定要清楚, 就目前看来,其实你的这个实习技术还不错。不要太焦虑。就是有一些内容有点虚了。可以考虑从PR中再投一点产出
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务