为什么不安全的.run()调用可以在Kotlin的null值上正常工作? [英] Why do unsafe .run() call works fine on a null value in Kotlin?
问题描述
我有以下代码片段:
val foo: String? = null
foo.run { println("foo") }
我这里有一个可为空的变量foo
,该变量实际上被设置为null
,随后是一个不安全的.run()
调用.
I have here a nullable variable foo
that is actually set to null
followed by a nonsafe .run()
call.
运行代码段时,尽管在null
上调用了run
方法,但仍打印出了foo
.这是为什么?为什么没有NullPointerException
?为什么编译器允许对可选值进行非安全调用?
When I run the code snippet, I get foo
printed out despite the fact that the run
method is called on a null
. Why is that? Why no NullPointerException
? Why does compiler allow a nonsafe call on an optional value?
如果我通过了println(foo)
,则在控制台中会得到很好的null
,因此我认为可以肯定地认为foo
实际上是null
.
If I pass println(foo)
, I get a nice juicy null
in the console so I think it's safe to assume that foo
is actually null
.
推荐答案
我相信,有两件事可能都令人感到意外:允许此类调用的语言语义,以及在执行此代码时在运行时发生的事情
I believe, there are two things that both might be of some surprise: the language semantics that allow such a call, and what happens at runtime when this code executes.
从语言的角度来看,Kotlin允许可为空的接收器,但仅适用于扩展.要编写一个接受可为空的接收器的扩展函数,要么显式地编写可为空的类型,要么为类型参数使用可为空的上限(实际上,当您不指定上限时,默认值是可为空的Any?
):
From the language side, Kotlin allows nullable receiver, but only for extensions. To write an extension function that accepts a nullable receiver, one should either write the nullable type explicitly, or use a nullable upper bound for a type parameter (actually, when you specify no upper bound, the default one is nullable Any?
):
fun List<*>?.isEmptyOrNull() = this == null || size == 0 // explicit nullable type
fun <T : CharSequence?> T.nullWhenEmpty() = if ("$this" == "") null else this // nullable T
fun <T> T.identity() = this // default upper bound Any? is nullable
在kotlin-stdlib
中的多个地方都使用了此功能:请参见 CharSequence?.isNullOrBlank()
, String?.orEmpty()
,甚至是 Any?.toString()
.某些功能,例如 T.let
, T.run
和其他一些只是不提供鞋帮绑定到type参数,默认为可为空的Any?
.并且 T.use
提供了可为空的上限Closeable?
This feature is used in kotlin-stdlib
in several places: see CharSequence?.isNullOrEmpty()
, CharSequence?.isNullOrBlank()
, ?.orEmpty()
for containers and String?.orEmpty()
, and even Any?.toString()
. Some functions like T.let
, T.run
that you asked about and some others just don't provide an upper bound for the type parameter, and that defaults to nullable Any?
. And T.use
provides a nullable upper bound Closeable?
.
在后台,即从运行时的角度来看,扩展调用不是 编译为JVM成员调用指令 INVOKEINTERFACE
或 INVOKESPECIAL
(JVM检查此类调用的第一个参数(隐式this
)是否为null,如果存在则抛出NPE,这就是Java&称为Kotlin成员函数).而是将Kotlin扩展功能编译为静态方法,并且将接收方作为第一个参数传递.可以使用 INVOKESTATIC
调用这种方法不检查参数是否为空的指令.
Under the hood, that is, from the runtime perspective, the extension calls are not compiled into the JVM member call instructions INVOKEVIRTUAL
, INVOKEINTERFACE
or INVOKESPECIAL
(the JVM checks the first argument of such calls, the implicit this
, for being null and throws an NPE if it is, and this is how Java & Kotlin member functions are called). Instead, the Kotlin extension functions are compiled down to static methods, and the receiver is just passed as the first argument. Such a method is called with the INVOKESTATIC
instruction that does not check the arguments for being null.
请注意,当扩展的接收者可以为空时,Kotlin不允许您在需要非空值的情况下使用它,而无需先将其检查为空:
Note that when a receiver of an extension can be nullable, Kotlin does not allow you to use it where a not-null value is required without checking it for null first:
fun Int?.foo() = this + 1 // error, + is not defined for nullable Int?
这篇关于为什么不安全的.run()调用可以在Kotlin的null值上正常工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!