科特林:为什么!!运算符在编译jvm时生成空值检查?

时间:2017-05-13 22:54:21

标签: java jvm kotlin

我正在使用https://javap.yawk.at/来检查Kotlin生成的字节码。我发现每当!!使用运算符,生成相应的空检查。例如,对于以下kotlin代码:

fun main(args: Array<String>) {
    var a : Int? = null;
    println(a!!+2)
}

生成此代码:

public final class MainKt {
    public static final void main(@NotNull final String[] args) {
        Intrinsics.checkParameterIsNotNull((Object)args, "args");
        final Integer n;
        final Integer a = n = null;
        if (n == null) {
            Intrinsics.throwNpe();
        }
        System.out.println(((Number)n).intValue() + 2);
    }
}

我的问题是:为什么需要生成空检查。当JVM遇到空接收器上的方法调用时,是否会抛出NPE?为什么需要进行另一次冗余检查?

3 个答案:

答案 0 :(得分:9)

!!表达式的语义是它在使用!!运算符的特定位置执行空检查,并抛出特定类型的异常(KotlinNullPointerException,如果要检查的表达式为null,则为标准NullPointerException的子类。抛出一个特定的异常子类可以清楚地表明异常是由!!运算符中的空检查失败引起的,因此可以更容易地跟踪生产中发生的原因。

即使Java确实在后续表达式中抛出NPE(它可能会或可能不会,取决于您如何使用应用!!的值),它将是一个通用的NPE,而不是特定的Kotlin。此外,堆栈跟踪中的行号可能与使用!!的行号不同,这使得理解问题变得更加困难。

答案 1 :(得分:1)

问题是JVM不能足够早地抛出NPE。您不必立即使用非空类型。使用空断言可以防止null通过代码传播太远而无法跟踪。假设你有这个类

class Foo {
    var bar : Bar = null
    fun foo() {doSomethingWitNonNullBar(bar)}
}

此处栏在Foo中设置后不会立即使用。如果您不小心写了类似foo.bar = null的内容并稍后在另一个线程上调用了foo.foo(),那么很难知道这个null来自哪里。这可能看起来很愚蠢,但当你的代码混合了许多可空类型和非空类型时,它很快就会成为一个真正的交易。如果没有断言,如果类型不为空,你就无法自信。

答案 2 :(得分:-2)

我只看到一个原因:字符串操作。

IN_ARRAY2