我正在使用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?为什么需要进行另一次冗余检查?
答案 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