Kotlin刨根问底——你真的了解Kotlin中的空安全吗?

前言

Android原生开发,越来越多的公司加上「熟悉Kotlin」这条招聘要求,被如火如荼的Flutter浪潮淹没,大家可能都忘了,在今年五月的Google I/O大会上,官方曾宣布:「Kotlin编程语言是Android应用开发的首选语言」!

初次接触Kotlin已是三年前,在上家公司用Kotlin重构了平板的应用市场和电台APP。说来惭愧,至今还是一个API选手,不过相信在座很多Android开发和我一样,都停留在会用的程度。

不想止步于会用,还想了解下原理,所以有了这个系列。先来刨一刨Kotlin中的空安全~

0x2、Kotlin里的空安全怎么用

NullPointerException(NPE),空指针异常,在Java开发时,如果不想遇到这种异常,需要在使用前不停地判断它是否为Null。而在Kotlin中是空安全的,在编译期就处理Null,以此避免执行时异常。另外空安全不是Kotlin特有的,其他很多的变成语言也有,接着简单描述下Kotlin空安全的相关用法。

① 非空类型与可空类型

Kotlin中通过「非空类型」和「可空类型」来规避空NPE,非空类型不能设置为Null值:
在这里插入图片描述
可空类型可以设置为Null值,在类型后加上 可空操作符(?) 即可,代码示例如下:
在这里插入图片描述
可空类型,直接访问它的属性或方法,会报错:
在这里插入图片描述
可以通过 安全调用操作符(?.) 或 非空断言运算符(!!) 来解决,代码示例如下:
Kotlin刨根问底——你真的了解Kotlin中的空安全吗?_第1张图片
运行结果如下:
在这里插入图片描述
② Elvis操作符(??

三目条件运算符的简略写法:如果不是空,就返回它,否则返回另一个值。代码示例如下:
在这里插入图片描述
③ 安全的类型转换

Kotlin中可以使用as关键字来进行类型转换,而使用as?表示安全类型的转换。最常见的使用场合,后台接口返回一个参数,需要我们自己做下类型转换,直接用as的话,如果参数为null或者非String类型,就可能引发异常,如果用as? ,参数异常则不会转换,代码示例如下:
Kotlin刨根问底——你真的了解Kotlin中的空安全吗?_第2张图片
运行结果如下:
在这里插入图片描述
④ ?.let{}

let函数除了可用于在同一个作用域下操作变量外(代码更优雅)外:

tvTitle.let {    it.text = "标题"    it.textSize = 18.0f    it.gravity = Gravity.LEFT or Gravity.CENTER    it.setOnClickListener {}}

还可以用作做判null操作,比如下面这样的代码:
Kotlin刨根问底——你真的了解Kotlin中的空安全吗?_第3张图片
test我们已经设置了一个值,但是还是报错,添加上一个非空断言即可:
Kotlin刨根问底——你真的了解Kotlin中的空安全吗?_第4张图片
还有个更优雅的写代码方式,就是用?.let{},修改后的代码如下:
Kotlin刨根问底——你真的了解Kotlin中的空安全吗?_第5张图片
另外?.let{}遍历集合会忽略null值,只对非null值执行操作;除此之外,还可以用集合操作函数filterNotNull过滤出非空元素。

0x3、不是绝对的空指针安全

如题,Kotlin中并不是绝对的空指针安全,最常见的就是在Kotlin去调Java代码,比如下面这个例子:

/* Javapublic class Test {    public static String getMsg() {        return null;    }}//Kotlinfun main() {    println(Test.getMsg().length)} */

运行后就直接报错:
Kotlin刨根问底——你真的了解Kotlin中的空安全吗?_第6张图片
当你与Java代码进行互操作时,Null安全性确实被破坏了,当然想规避这个问题也很简单,加个?即可,示例如下:

fun main()
{
	println( Test.getMsg() ?.length )
}

运行结果如下:

0x4、Kotlin是如何实现空安全的

接着我们来探究下,Kotlin中到底是怎么实现空安全的,写下简单的代码:

fun test_1( str : String ) = str.lengthfun test_2( str : String ? ) = str ?.lengthfun test_3( str : String ? ) = str !!.lengthfun test_4( str : Any ? )
{
	str as String
}


fun test_5( str : Any ? )
{
	str as ? String
}

接着依次点击:Tools -> Kotlin -> Show Kotlin Bytecode,生成字节码:
Kotlin刨根问底——你真的了解Kotlin中的空安全吗?_第7张图片
生成后的Java代码如下:
Kotlin刨根问底——你真的了解Kotlin中的空安全吗?_第8张图片
接着分析一波,首先是test_1函数,可以看到这里有一个@NotNull的注解,然后是:

Intrinsics.checkParameterIsNotNull( str, "str" );

Intrinsics是Kotlin的一个内部类,定位到checkParameterIsNotNull函数:
Kotlin刨根问底——你真的了解Kotlin中的空安全吗?_第9张图片
再跟:
Kotlin刨根问底——你真的了解Kotlin中的空安全吗?_第10张图片
行吧,其实就是直接对参数判空,如果为空抛出参数为空的异常。接着看下test_2函数,比较简单,判断是否为空,不为空的话,调用对应的方法,否则返回一个null。再接着是test_3函数,直接判断是否为空,空的话直接抛出Npe异常。
在这里插入图片描述
然后是test_4函数,判空,如果空抛出类型转换异常,否则执行后续代码;最后是test_5函数,创建一个新对象把参数的值传给他,然后进行类型判断,如果不是特定类型,对象赋值null,然后把执行类型强转后的对象赋值给一个新的对象。
综上,Kotlin中对空安全背后的处理套路如下:

1、非空类型的属性编译器添加@NotNull注解,可空类型添加@Nullable注解; 2、非空类型直接对参数进行判空,如果为空直接抛出异常;
3、可空类型,如果是?.判空,不空才执行后续代码,否则返回null;如果是!!,空的话直接抛出NPE异常。
4、as操作符会判空,空的话直接抛出异常,不为空才执行后续操作,没做类型判断!运行时可能会报错!
5、as?则是新建一个变量,参数赋值给它,然后判断是否为特定类型,赋值为null,接着把这个变量的值赋值给另一个新的变量,这里有一点注意:as?处理后的参数可能为空!!!所以调用as?转换后的对象还需要添加安全调用操作符(?.)

提醒:(这里直接用编译后Java代码的原因是比较直观~)

@NotNull注解对应字节码里的:@Lorg/jetbrains/annotations/NotNull;
@Nullable注解对应字节码里的:@Lorg/jetbrains/annotations/Nullable;

以上就是本人关于Kotlin中空安全实现原理的解读,如果纰漏或建议,欢迎在评论区指出,谢谢~
Kotlin刨根问底——你真的了解Kotlin中的空安全吗?_第11张图片

你可能感兴趣的:(安卓日常)