0x3、不是绝对的空指针安全
如题,Kotlin中并不是绝对的空指针安全,最常见的就是在Kotlin去调Java代码,比如下面这个例子:
//Java public class Test { public static String getMsg() { return null; } } //Kotlin fun main() { println(Test.getMsg().length) }
运行后就直接报错:
当你与Java代码进行互操作时,Null安全性确实被破坏了,当然想规避这个问题也很简单,加个?即可,示例如下:
fun main() { println(Test.getMsg()?.length) }
运行结果如下:
Tips:也可以为Java代码@NotNull注解来解决~
0x4、Kotlin是如何实现空安全的
接着我们来探究下,Kotlin中到底是怎么实现空安全的,写下简单的代码:
fun test_1(str: String) = str.length fun test_2(str: String?) = str?.length fun test_3(str: String?) = str!!.length fun test_4(str: Any?) { str as String } fun test_5(str: Any?) { str as? String }
接着依次点击:Tools -> Kotlin -> Show Kotlin Bytecode,生成字节码:
生成后的Java代码如下:
接着分析一波,首先是test_1函数,可以看到这里有一个@NotNull的注解,然后是:
Intrinsics.checkParameterIsNotNull(str, "str");
Intrinsics是Kotlin的一个内部类,定位到checkParameterIsNotNull函数:
再跟:
行吧,其实就是直接对参数判空,如果为空抛出参数为空的异常。接着看下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;