【问题标题】:Why does the system display 'Smart cast to 'File' is impossible' in Kotlin?为什么系统在 Kotlin 中显示“Smart cast to 'File' is not possible”?
【发布时间】:2019-11-05 02:31:29
【问题描述】:

在代码 A1 中我使用了let 语句,所以我认为filenameofVideo.path 不会为空

但我得到以下错误,为什么?

智能转换为“文件”是不可能的,因为“filenameofVideo”是一个可变属性,此时本可以更改

此时,我必须使用 Code A2。

代码 A1

private var filenameofVideo :File?=null

filenameofVideo?.let {
          Navigation.findNavController(requireActivity(), R.id.fragment_container)
               .navigate(UIFragmentCameraDirections.actionCameraToVideo(filenameofVideo.path))
}

代码 A2

private var filenameofVideo :File?=null

filenameofVideo?.let {filenameofVideo ->
        Navigation.findNavController(requireActivity(), R.id.fragment_container)
            .navigate(UIFragmentCameraDirections.actionCameraToVideo(filenameofVideo.path))
}

此外,我发现代码 B1 和代码 B2 都是正确的。为什么代码 B1 正确,代码 A1 错误?

代码 B1

   private val aa:String?=null
   aa?.let {
            print(aa)
        }

代码 B2

private val aa:String?=null    
   aa?.let{aa->
           print(aa)
      }

添加内容:

1:在代码 C 中,在 ?.let 调用中访问 var aa 和在 let 块中访问它之间,var aa 可能已被更改(可能被另一个线程更改)。

aa不为null时会启动codec,aaa为null时不会启动codec吧?

2:在代码D中(我假设编译器接受它),无论aa是否为null,函数总是被启动,它不能被接受,所以系统会中断,对吗?

代码 C

private var aa: String? = null    
aa?.let { kk ->
    print(kk.length)
}

代码 D

private var aa: String? = null    
aa?.let {
    print(aa.length)
}

【问题讨论】:

标签: android kotlin


【解决方案1】:

让我们稍微精简一下您的代码,以便更容易讨论:

private var filename :File?=null

filename?.let {
    action(filename.path)
}

这里的问题是,即使您调用了?.let,编译器也不能保证filenamelet 块内不为空。这是因为filename 是一个var,并且var 的值可能在?.let 调用中访问它的那一刻和它在let 块。

因此,您必须自己处理 null 的可能性。最简单的方法是在 let 块中使用 it 值,或者自己命名该值:

filename?.let { safe ->
    action(safe.path)
}

这段代码起作用的原因:

private val aa:String?=null
aa?.let {
    print(aa)
}

这里的变量是val。这意味着它将是null 或非空值,但无论哪种方式它都无法更改。所以你和我可以阅读代码并看到.let 块永远不会被执行......但如果它以某种方式,那将意味着aa 保证不会为空,因此编译器可以将其智能转换为不可为空的类型。

【讨论】:

  • 谢谢!请您看看我在问题中添加的内容吗?
【解决方案2】:

类似的代码

private var aa: String? = null
aa?.let {
    print(aa.length)
}

无法编译,因为虽然安全调用 ?.let { } 块仅在 aa 属性不为空时执行,但在执行到达 aa.length 时,aa 属性的值可能已经更改在另一个线程中为 null。

另一方面,在这个例子中

private var aa: String? = null    
aa?.let { aa ->
    print(aa.length)
}

aa 属性的非空值是在传递给let 的 lambda 的只读 aa 参数中安全调用后捕获的。

请注意,将aa 属性设置为val 而不是var 如在您的示例B1 中也有助于消除错误,但这会导致let 块永远不会执行,因为初始化为null 的私有val 永远不会变为非空。

关于示例 C 和 D 之间有什么区别的附加问题:

  • let { } 函数的执行方式没有区别。在这两种情况下,只有当接收者表达式 aa 不为空时才会启动 lambda 函数,因为您使用了 ?. 安全调用运算符。
  • 编译器不接受示例 D,因为它无法确保在 lambda 中引用的 aa 属性的值在执行到达该行时没有更改。

【讨论】:

  • 谢谢!请您看看我在问题中添加的内容吗?
  • 我已经编辑了答案来解决这个额外的问题。
猜你喜欢
  • 1970-01-01
  • 2020-09-25
  • 1970-01-01
  • 1970-01-01
  • 2021-02-27
  • 2022-12-28
  • 2021-06-01
  • 2020-06-05
  • 1970-01-01
相关资源
最近更新 更多