在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的实现思路,然后查看其字节码和类文件实现就能一探究竟。
网友评论