原理"/>
Kotlin空安全原理
前沿
可能又要闲置一段时间了,闲置的原因长的不知道从何说起,收拾收拾心情,整理下博客,不卑不亢,静下心来,梳理下知识点,写写博客记录下,就当是给自己放假啦,浮躁的人得静下心来,能力不够学习来凑!
kotlin空安全
被面试官问到kotlin空安全的原理,瞬间触碰到盲区了,自己用过kotlin也知道kotlin空安全的用法以及优点,就是没有想过它的原理是什么,然后卒。
回去特意查了下,kotlin对空字符串以及null的判断要比Java友好的多,相比较Java的TextUtils.isEmpty()方法,我们看看kotlin的校验空字符串的几个方法:
- isNullOrEmpty: 为空指针或者字符串长度为0时返回true,非空字符串和可空字符串都可以调用。
- isNullOrBlank: 为空指针、字符串长度为0或者全为空格时候返回true,非空字符串和可空字符串都可以调用。
- isEmpty: 字符串长度为0时返回true,只有非空字符串的时候才可以调用。
- isBlanK: 字符串长度为0或者全为空格时返回true,只有非空字符串才可以调用
- isNotEmpty: 字符串长度大于0时返回true,只有非空字符串可以调用。
- isNotBlank: 字符串长度大于0且不是全空格时返回true,只有非空字符串可以调用
定义可变变量
从上面的定义可以看出来,kotlin判断空值的时候要区分空串和非空字符串的,这是由于Kotlin的空安全概念,每个变量都可以定位为可以是空和不可以为空这两种。
定义可为空变量
private var name:String?=null
定义不可为空变量
private var age = "18"
从这里好像看不出来有什么不同,接下来我们分别获取他们的长度:
//定义可为空变量val nameLength = name?.lengthval nameLength02 =name?.length?:0val nameLength01 = name!!.length//定义不为空变量 val ageLength=age.length
可以看到当不可以为空时,直接获取长度,而可为空时kotlin有几种比较常用的写法
第一种
val nameLength = name?.length
//类似Java
if(nameLength!=null)nameLength=name.length();
else nameLength=null;
第二种
val nameLength02 =name?.length?:0
如果为null的话我们可以自定义一个默认值而不是直接返回null
第三种
val nameLength01 = name!!.length
//!!表示强行放弃空安全判断默认值肯定不为空,当然遇到空值肯定闪退
看我们定义一个不可为空的value值来检验下:
可以清楚看到 nameLength编译不通过,因为它存在为null的情况故编译器报错了。
而nameLength01我们用 !! 修饰的即便存在null的情况,编译器不去检查编译也可以通过
而nameLength02 我们通过 ?: 来给一个非null的默认值故通过编译
ageLength 为非空肯定没问题!
到这我们可以总结下kotlin的空安全概念
- 声明变量时,在类型后面加问号时,表示该变量可以为空;
- 调用变量方法时,在变量名后面加问号,表示如果为空就返回null;
- 当使用 ?: 运算符时表示如果变量为null 时返回我们运算符右边定义的值;
- 当使用 !! 运算符时,表示通知编译器不做非空校验,如果运行时发现变量为空时,抛出异常。
好了普及到这终于到重点了
kotlin 实现空安全判断的原理
因为这些都是关键字,只能从kotlin编译的.class文件下手了。
查看方式依次点击:Tools->Kotlin->Show Kotlin ByteCode
我写了三个测试类如下:
class TestDome {fun Test01(name:String){name.length}fun Test02(name:String?){name?.length}fun Test03(name:String?){name!!.length}}
可以看到对应的Kotlin Bytecode
//方法Test01public final Test01(Ljava/lang/String;)V// annotable parameter count: 1 (visible)// annotable parameter count: 1 (invisible)@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0通过注解标示参数是否可以为nullL0ALOAD 1LDC "name"INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V 如何不为null 则检查是否被复null值L1LINENUMBER 11 L1ALOAD 1INVOKEVIRTUAL java/lang/String.length ()IPOPL2LINENUMBER 12 L2RETURNL3LOCALVARIABLE this Lcom/example/myapplication/kotin/TestDome; L0 L3 0LOCALVARIABLE name Ljava/lang/String; L0 L3 1MAXSTACK = 2MAXLOCALS = 2//方法Test02public final Test02(Ljava/lang/String;)V// annotable parameter count: 1 (visible)// annotable parameter count: 1 (invisible)@Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0 表示可以为空值L0LINENUMBER 15 L0ALOAD 1DUPIFNULL L1 //如果是null 执行L1即Pop方法INVOKEVIRTUAL java/lang/String.length ()IPOPGOTO L2L1POPL2L3LINENUMBER 16 L3RETURNL4LOCALVARIABLE this Lcom/example/myapplication/kotin/TestDome; L0 L4 0LOCALVARIABLE name Ljava/lang/String; L0 L4 1MAXSTACK = 2MAXLOCALS = 2
//方法Test03public final Test03(Ljava/lang/String;)V// annotable parameter count: 1 (visible)// annotable parameter count: 1 (invisible)@Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0L0LINENUMBER 19 L0ALOAD 1DUPIFNONNULL L1//如果不是null 执行L1即java/lang/String.length ()否则抛出异常INVOKESTATIC kotlin/jvm/internal/Intrinsics.throwNpe ()VL1INVOKEVIRTUAL java/lang/String.length ()IPOPL2LINENUMBER 20 L2RETURNL3LOCALVARIABLE this Lcom/example/myapplication/kotin/TestDome; L0 L3 0LOCALVARIABLE name Ljava/lang/String; L0 L3 1MAXSTACK = 2MAXLOCALS = 2
仔细对比字节码的朋友会发现
Test01方法入口下面的注解是:
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 可以看到是NotNull
而Test02、Test03是:
@Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0 可以看到是Nullable
那对于Test01方法注 解是 NotNull我们传入一个null值编译器是又如何知道报错的呢?
kotlin编译器内部调用了是否为null的检查,这就是为什么我们传入null的时候会编译报错
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
而Test02、Test03方法是不会执行这个检查的,但是Test02和Test03也是有区别的:
Test02:
LINENUMBER 15 L0
ALOAD 1
DUP
IFNULL L1
INVOKEVIRTUAL java/lang/String.length ()I
POP
GOTO L2
L1
POP
L2
L3
LINENUMBER 16 L3
RETURN
可以看出如果是null的话直接执行POP而不去执行String.length()方法;
而
Test03方法多了个异常抛出的方法
INVOKESTATIC kotlin/jvm/internal/Intrinsics.throwNpe ()V
也就是当name为null时直接抛出异常。
到这通过查看三个方法的字节码,我们可以总结下Kotlin空安全原理:
- 首先通过注解 @Lorg/jetbrains/annotations/NotNull;和@Lorg/jetbrains/annotations/Nullable;来向编译器标示参数是否为空,如果不为空则通过 INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)来进行检查,此时如果我们给个空值则编译器出现报错提示。
- 对用使用 ?. 操作符号kotlin会判断是否为null 如果不为null执行对应的逻辑,如果为null则什么也不执行(此时的默认结果也是null)
- 对用使用 !! 操作符号 Kotlin 同样会执行null判断如果不为null 则执行对应的逻辑,如果为null 则抛出异常,即执行 INVOKESTATIC kotlin/jvm/internal/Intrinsics.throwNpe ()
更多推荐
Kotlin空安全原理
发布评论