为什么系统在Kotlin中显示“无法将智能类型转换为'文件'”?

时间:2019-11-05 02:31:29

标签: android kotlin

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

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

不可能将其强制转换为“文件”,因为“ filenameofVideo”是可变的属性,这一次可能已经更改了

此刻,我必须使用代码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调用中访问它的时刻与在let块中访问它的时刻之间,var aa可能已被更改(也许被另一个线程)。

aa不为null时将启动代码C,而aa为null时将不启动代码C,对吗?

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)
}

2 个答案:

答案 0 :(得分:2)

让我们简化一下代码,以便于讨论:

private var filename :File?=null

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

这里的问题是,即使您已调用?.let,编译器也不能保证filename块内的let不为null。这是因为filenamevar,并且在var调用中访问?.let的值之间可能已更改(也许是由另一个线程)以及在let块中对其进行访问的时间。

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

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

此代码有效的原因:

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

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

答案 1 :(得分:1)

类似的代码

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

之所以无法编译,是因为尽管仅在?.let { }属性不为null的情况下执行安全调用aa块,但在执行达到aa.length时,{{1 }}属性可能已在另一个线程中更改为null。

另一方面,在此示例中

aa

安全调用之后,在传递给{{1}的lambda的只读private var aa: String? = null aa?.let { aa -> print(aa.length) } 参数中捕获了aa属性的非null值。

请注意,使aa属性为let而不是示例中的aa可以帮助消除错误,但这会导致val块从不执行初始化为null的私有val永远不会变为非null。

关于示例C和示例D有什么区别的另一个问题:

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