Kotlin 1.3.11是否破坏了空安全性?

6
fun handle() : String {
    null?.let { return "Ololo"}
}

val result = handle()
result.trim() // kotlin.TypeCastException: null cannot be cast to non-null type kotlin.CharSequence

有什么想法,为什么 Kotlin 的 null 安全函数会返回 null?

null?.let { }是什么意思? - DawidJ
@DawidJamroży 在实际使用中,我有一个变量而不是硬编码的 null。 - Nickolay Savchenko
1
似乎是 Kotlin 类型系统的一个 bug。这似乎不应该编译,除非返回类型是 String? - RussHWolf
1
@Bruce,它根本不应该编译。let()只有在其引用不为null时才应运行,这意味着实际上缺少了一个return语句。不过,目前似乎编译器只是添加了一个return null - TheWanderer
是的,你说得对。我把它读成了 return null?.let { ... } - RussHWolf
显示剩余3条评论
3个回答

9
这是由于在 Kotlin 1.3 中引入 contracts 用于标准函数 letrunapplyalso 导致的错误。
修复计划针对版本 1.3.20。有关详细信息,请参见 KT-28061

谢谢回复,很好。 - Nickolay Savchenko

4
看起来 Kotlin 编译器在 let 不执行的情况下添加了 null 返回。这可能是一个 bug,因为它不应该被编译,并且在之前的 Kotlin 版本中也不会被编译。
如果只编译您的示例,我们会得到以下结果:
@NotNull
public final String handle() {
    return null;
}

我认为这只是编译器的优化,因为null?.let()永远不会被执行。

使用实际变量会产生以下结果:

@NotNull
public final String handle() {
    return someNullableVariable != null ? "Ololo" : null;
}

换句话说,如果let()的引用是null,则不会执行它。然而,由于该函数需要返回某些内容,编译器只能告诉它返回null,因为没有其他可返回的东西。
由于该函数被标记为@NotNull,Kotlin将对任何引用该函数的内容执行空值检查:
fun someOtherMethod() {
    handle().trim()
}

变成

public final void someOtherMethod() {
    String handle = handle();

    if (handle != null) {
        StringsKt__StringsKt.trim(handle).toString();
        return;
    }

    throw new Exception("null cannot be cast to non-null type kotlin.CharSequence");
}

有两种处理方式。您可以将handle()的返回类型更改为String?

fun handle(): String? {
    someNullableVariable?.let { return "Ololo" }
}

如果变量为空,您可以返回其他内容:

fun handle(): String {
    someNullableVariable?.let { return "Ololo" }
    return "someNullableVariable was null"
}

2
感谢您的解释。看起来是Kotlin编译器的bug,因为在更早的版本中,显示的代码无法编译。在我看来,这是一种对空安全语言的预期行为。 - Nickolay Savchenko

3
这一定是个bug,因为:
  • 缺少一个返回语句(或更好的:表达式),因为传递给let的lambda不会被调用。
  • 一个以String为返回类型的函数不应该返回null

有趣的是,在Kotlin 1.2.x中甚至无法编译这段代码:
fun handle() : String {
    null?.let { return "Ololo"}
} 

错误:(6,0)在具有块体({...})的函数中需要“return”表达式

在Kotlin 1.3.11中需要。

无论如何:

let不会被调用,因为安全调用运算符?评估为null(在这种情况下)。


2
在 Android Studio 3.4 上,我使用 Kotlin 1.3.11 编译成功。 - TheWanderer
@TheWanderer 确实,我把它粘贴到了在线 Kotlin Playground 中,并意外使用了版本 1.2.71。谢谢。 - Willi Mentzel
2
这真的很奇怪/有趣;在let块内部仅使用return,如果未执行该块,则不会返回任何内容。由于它从未执行,因此实际上没有返回任何内容。fun handle() : String { }本来就不应该编译通过,因为存在不返回的可能性。也就是说,除非Kotlin现在返回默认值(如果未指定),但那将非常奇怪。看起来像是一个错误。 - Zoe stands with Ukraine
@Zoe,请检查我的答案。它添加了一个空返回。 - TheWanderer
1
@TheWanderer 是的,但它不应该。代码中没有任何部分会有一个备用返回。 - Zoe stands with Ukraine
@Zoe 真的很奇怪 :D - Willi Mentzel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接