【问题标题】:Nullable var and smart cast可空变量和智能转换
【发布时间】:2020-08-30 11:25:08
【问题描述】:

考虑以下 Kotlin 块。

var nullableInt: Int? = null

if (nullableInt != null) {
    val checkedInt: Int = nullableInt
    print("not-null-branch")
} else {
    print("null-branch")
}

Android Studio 告诉我从Int?Int 的智能转换是不可能的,因为nullableInt 是可变的。我知道这可能是多线程代码中的问题。

处理该问题的一种方法是使用 val checkedInt: Int = nullableInt!! 进行显式转换,但如果我将在多线程环境中使用编码,则不可取。


关闭重复项

关于这个主题有几个非常接近的问题。但是,在我找到的任何一个中,我都没有找到令人满意的答案:

In Kotlin, what is the idiomatic way to deal with nullable values, referencing or converting them 讨论了问题出现的原因,但没有提供如何处理的建议

Kotlin avoid smart cast for null check 有一个 if-not-null 分支,它返回一个非空值,所以 ?.let{} :? {} 构造在那里工作。由于我的 not-null-branch 返回 null,因此两个分支都会运行。

Kotlin "Smart cast is impossible, because the property could have been changed by this time" 只关注非空分支和非空分支,因此?.let{} 构造似乎是正确的。在那个线程中,他们提供了关于在 if 语句之前获取本地副本的建议,这在我的情况下也是可行的。不幸的是,它不是很优雅,我希望有其他的选择。


有没有什么方法可以在不复制的情况下以 null 安全的方式处理这种 null 条件分支?

我知道答案可能是“视情况而定”。如果是这样,请说出来并详细说明原因。

【问题讨论】:

    标签: kotlin null kotlin-null-safety


    【解决方案1】:

    使用.let 而不是?.let

    因为.let 扩展函数是为所有类型定义的,包括可为空的类型,你实际上可以在没有安全调用?. 运算符的情况下调用它。当您这样做时,将始终调用 lambda,即使对于 null 值也是如此。如果接收者可以为空,则let 块内的参数也可以为空。

    但是,lambda 参数适合智能转换,因为它不是可变的。

    这就是区别:

    x.let { it -> /* This always runs. 'it' can be null if 'x' is null */ }
    x?.let { it -> /* This only runs if 'x' is not null. 'it' is never null. */ }
    

    将其应用于您的示例,您可以这样写:

    var nullableInt: Int? = null
    
    nullableInt.let {
        if (it != null) {
             doSomethingWith(it)
        } else {
             doSomethingElse()
        }
    }
    

    【讨论】:

      【解决方案2】:

      您不能开始转换属性,因为另一个线程可能正在修改它。没有合乎逻辑的方法。该语言已经提供了一种不安全的方式来使用您已经提到的!!

      制作引用的本地副本(手动或使用withlet 等范围函数)非常简单,因此您无需担心。

      我个人的看法是,局部变量是最干净、最易读的方法。您可以避免嵌套使用范围函数的块。

      val myVar = myProp
      if (myVar != null) {
      
      } else {
      
      }
      

      我认为作用域函数最简洁的方法是使用with。当您同时处理两个分支时,它的阅读效果比 let 更好。

      with(myProp) {
          if (this != null) {
      
          } else {
      
          }
      }
      

      为了以简洁的方式处理这两个分支,可以使用以下方法。在这种情况下,我认为alsolet 更健壮一点,因为您不能通过从第一个 lambda 返回 null 来意外运行两个分支。但是如此简洁会影响可读性。

      myProp?.also {
          // not null it
      } ?: run {
          // null
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-11-19
        • 2019-07-18
        • 2018-03-23
        • 2018-07-07
        • 2022-08-09
        • 2010-11-09
        相关资源
        最近更新 更多