为什么不安全的.run()调用可以在Kotlin的null值上正常工作? [英] Why do unsafe .run() call works fine on a null value in Kotlin?

查看:152
本文介绍了为什么不安全的.run()调用可以在Kotlin的null值上正常工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码片段:

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中的多个地方都使用了此功能:请参见 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成员调用指令 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆