【问题标题】:How to pass nullable type into function that takes a non null type?如何将可空类型传递给采用非空类型的函数?
【发布时间】:2015-04-23 18:39:52
【问题描述】:

如果我在通过之前进行空检查,这可能吗?例如:

fun main(args: Array<String>) {
    var num: Int? = null
    // Stuff happens that might make num not null
    ...
    if (num != null) doSomething(num)
}

fun doSomething(number: Int) {
    ...
}

我不明白为什么编译器不允许我传递一个可为空的,即使我首先检查它不是空的。谁能解释一下?

【问题讨论】:

  • 您的具体情况在当前的 Kotlin 中不再是问题,编译器可以确定变量发生变异的机会与否。请参阅答案:stackoverflow.com/a/34599753/3679676,或涵盖可空性和 Kotlin 运算符所有方面的其他问题:stackoverflow.com/a/34498563/3679676
  • 另外,如果你遇到编译器(正确地)不能假设它是非空的情况之一,你总是可以添加 !!到变量名的末尾,告诉 Kotlin 你相信它不为空。

标签: kotlin


【解决方案1】:

使用可以使用范围函数 let 或 apply 以及空安全运算符 ?。

 fragmentManager?.let{
        viewPager.adapter = TasksPagerAdapter(it)
    }

这样你可以将一个可为空的类型传递给一个不可为空的类型参数

【讨论】:

    【解决方案2】:

    您可以使用let 来简化代码。 kotlin 作用域函数在“num”的上下文中引入了一个局部变量。无需声明临时变量numVal

    fun main(args: Array<String>) {
        var num: Int? = null
        // Stuff happens that might make num not null
        ...
        num?.let{
            doSomething(it)
        }
    }
    

    它的工作方式与下面相同,但更简单、更干净。

    fun main(args: Array<String>) {
        var num: Int? = null
        // Stuff happens that might make num not null
        ...
        val numVal: Int? = num
        if (numVal != null) doSomething(numVal)
    }
    

    【讨论】:

      【解决方案3】:

      注意:从编译器版本 1.0 beta 开始,相关代码按原样工作

      编译器可以判断变量是否在检查和使用之间发生了变异,至少在这个问题中的局部变量的情况下,以及在其他一些情况下。详情请见Jayson's answer


      http://kotlinlang.org/docs/reference/null-safety.html#checking-for-null-keyword--in-conditions

      编译器跟踪有关 [null] 检查的信息...这仅适用于 b 不可变的情况(即本地 val 或具有支持字段且不可覆盖的成员 val),否则可能会发生b 检查后变为 null。

      所以这样的事情应该可以工作:

      fun main(args: Array<String>) {
          var num: Int? = null
          // Stuff happens that might make num not null
          ...
          val numVal: Int? = num
          if (numVal != null) doSomething(numVal)
      }
      
      fun doSomething(number: Int) {
          ...
      }
      

      当然,最好将“事情发生”重写为这样一种方式,即您可以首先将num 变为val

      【讨论】:

      • 最后一部分很重要。如果你的“东西”由条件逻辑组成,你可以写 val num = if (condition) stuff else null
      • 这可以简化,首先它不再是当前 Kotlin 中的问题,因为编译器可以跟踪局部变量的变化。请在此答案的顶部添加注释以影响该影响。现在更复杂的是哪些案例有效,哪些无效;随着编译器的成熟,还会添加更多内容。
      【解决方案4】:

      在当前的 Kotlin(1.0 测试版或更高版本)中,您不再遇到此问题。您的代码将编译。 valvar 的局部变量可以安全地Smart Cast,因为编译器可以确定该值是否可能发生突变(例如在另一个线程上)。

      这是来自another Stack Overflow question 的摘录,涵盖了可空性的更多方面以及处理这些问题的 Kotlin 运算符。

      更多关于 null 检查和智能投射

      如果您使用null 检查保护对可空类型的访问,编译器会将smart cast 语句主体内的值设为不可为空。有一些复杂的流程不会发生这种情况,但对于常见的情况可以正常工作。

      val possibleXyz: Xyz? = ...
      if (possibleXyz != null) {
         // allowed to reference members:
         possiblyXyz.foo()
         // or also assign as non-nullable type:
         val surelyXyz: Xyz = possibleXyz
      }
      

      或者,如果您执行is 检查不可为空的类型:

      if (possibleXyz is Xyz) {
         // allowed to reference members:
         possiblyXyz.foo()
      }
      

      对于同样可以安全转换的“when”表达式也是如此:

      when (possibleXyz) {
          null -> doSomething()
          else -> possibleXyz.foo()
      }
      
      // or
      
      when (possibleXyz) {
          is Xyz -> possibleXyz.foo()
          is Alpha -> possibleXyz.dominate()
          is Fish -> possibleXyz.swim() 
      }
      

      有些东西不允许null检查到smart cast以供以后使用变量。上面的示例使用了一个在应用程序流程中绝不可能发生变异的局部变量,无论是val 还是var,这个变量都没有机会变异为null。但是,在编译器无法保证流分析的其他情况下,这将是一个错误:

      var nullableInt: Int? = ...
      
      public fun foo() {
          if (nullableInt != null) {
              // Error: "Smart cast to 'kotlin.Int' is impossible, because 'nullableInt' is a mutable property that could have been changed by this time"
              val nonNullableInt: Int = nullableInt
          }
      }
      

      变量nullableInt的生命周期并不完全可见,可能是从其他线程分配的,null检查不能将smart cast变成一个不可为空的值。有关解决方法,请参阅下面的“安全呼叫”主题。

      另一个不能被smart cast 信任而不发生变异的情况是具有自定义getter 的对象上的val 属性。在这种情况下,编译器无法看到改变值的原因,因此您将收到一条错误消息:

      class MyThing {
          val possibleXyz: Xyz? 
              get() { ... }
      }
      
      // now when referencing this class...
      
      val thing = MyThing()
      if (thing.possibleXyz != null) {
         // error: "Kotlin: Smart cast to 'kotlin.Int' is impossible, because 'p.x' is a property that has open or custom getter"
         thing.possiblyXyz.foo()
      }
      

      阅读更多:Checking for null in conditions

      【讨论】:

      • 几天前我写了这段代码,ide 要求我添加空检查: if (a=null) {a = initA()} invokeA(a) // 这里 ide 认为 a 可以为空,虽然 initA() 返回一个非空值
      • 评论不是提出新问题@user1453345的好地方。您的问题中缺少详细信息,并且评论不允许提供所需内容。解决您的问题的信息在上面。您可能对第二个到最后一个示例有一些变化(显然a 可能会发生变异或流分析无法确定是否可能)。如果不清楚,那么您需要更多交互式支持来了解原因,请尝试Kotlin community on Slack
      猜你喜欢
      • 1970-01-01
      • 2010-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-05
      • 2018-02-14
      • 2017-07-19
      • 1970-01-01
      相关资源
      最近更新 更多