Kotlin

Kotlin 中空类型判断原理分析

2020-04-21  本文已影响0人  两三行代码

 在Java编程语言中,NullPointerException或简称为NPE是最常见的异常,也称为《十亿美元的错误》
,使javaer深受其苦。如果在代码中到处判空则显得代码过于丑陋,代码量也会疾速膨胀。
 基于此,作为后来者的Kotlin则吸取了上述教训,在设计之初就将消除来自代码空引用的危险作为宗旨。
 在Kotlin中为解决空类型安全问题,引入了安全调用操作符,写做?. 如下所示:

var a = "Kotlin" //  1
var b: String? = null // 2
println(b?.length) //3
println(a?.length) // 4无需安全调用

对以上1处 为非空类型 其等价于以下示例 且不可对非空类型置为null

var a: String = "abc"
a = null // 编译错误

对以上2处 则表示为字符串的可空类型 默认为null 在3处调用处表示在b为非空情况下打印获取其长度

以上为对安全调用操作符简单使用讲解,详细描述可参考Kotlin 空安全

现在我们来思考一下?.背后的实现原理,它到底是如果实现空安全的呢?

Java和Kotlin都是基于JVM的编程语言,共同遵循JVM规范,试想在Java中的空类型判断都是如下所示:

if(b!=null){
//do something work
}else{
//do something work
}

那么Kotlin在底层实现上是否也是如此?或者说?.安全调用符是对上述判断的封装呢?让我们来撸段代码来一探究竟:

class DemoActivityK : AppCompatActivity() {

    var str1: String?  = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.demo_k)
        Logger.i("len ${str1?.length}")
    }
}

在代码编译后借助Bytecode Viewer 查看DemoActivityK.class文件即可,以下摘抄关键部分:

protected void onCreate(@Nullable final Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       this.setContentView(R$layout.demo_k);
       final StringBuilder append = new StringBuilder().append("len ");
       final String str1 = this.str1;
       Logger.i(append.append((str1 != null) ? Integer.valueOf(str1.length()) : null).toString(), new Object[0]);
   }

从上述代码中可以看出来对str1作了str1!=null的判断,然后获取字符串长度,和Java传统实现思路是一样的写法,只不过是现在Kotlin通过?.语法简化了实现方式。
进一步扩展查看以下对应的字节码部分

   L4 {
             new java/lang/StringBuilder
             dup
             invokespecial java/lang/StringBuilder.<init>()V
             ldc "len " (java.lang.String)
             invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
             aload0 // reference to self
             getfield com/khronos/modular1/kotlin/DemoActivityK.str1:java.lang.String //1
             dup //2
             ifnull L5   // 3
             invokevirtual java/lang/String.length()I
             invokestatic java/lang/Integer.valueOf(I)Ljava/lang/Integer;
             goto L6
         }
         L5 {
             f_new (Locals[2]: com/khronos/modular1/kotlin/DemoActivityK, android/os/Bundle) (Stack[2]: java/lang/StringBuilder, java/lang/String)
             pop
             aconst_null
         }
         L6 {
             f_new (Locals[2]: com/khronos/modular1/kotlin/DemoActivityK, android/os/Bundle) (Stack[2]: java/lang/StringBuilder, java/lang/Integer)
             invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lang/StringBuilder;
             invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
             iconst_0
             anewarray java/lang/Object
             invokestatic com/orhanobut/logger/Logger.i(Ljava/lang/String;[Ljava/lang/Object;)V
         }

可以看见上述 //1 //2 //3处为读取str1值写入栈顶 然后拷贝栈顶值再此压如栈顶 在 3处判断如果为null则走L5字节码片段,否则后续获取字符串长度 然后跳转L6处字节码片段。

至此我们已经对Kotlin安全调用符?. 已经有了一个比较完整的认识。其实Kotlin和Java都是基于JVM的编程语言,Kotlin的许多语法的底层实现原理可以类比Java的实现思路,然后查看其字节码和类文件实现就能一探究竟。

上一篇 下一篇

猜你喜欢

热点阅读