如何将可空类型传递给采用非空类型的函数?

How to pass nullable type into function that takes a non null type?

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

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

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

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

编译器可以判断变量是否在检查和使用之间发生了变化,至少在这个问题中的局部变量以及其他一些情况下是这样。有关详细信息,请参阅


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

The compiler tracks the information about the [null] check ... this only works where b is immutable (i.e. a local val or a member val which has a backing field and is not overridable), because otherwise it might happen that b changes to null after the check.

所以像这样的东西应该可以工作:

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

当然,重写 "stuff happens" 会更好,这样一开始就可以将 num 变成 val

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

以下是 的摘录,其中涵盖了可空性的更多方面以及处理这些问题的 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为非空值。有关解决方法,请参阅下面的 "Safe Calls" 主题。

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

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

您可以使用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)
}

可以使用作用域函数 let 或 apply 以及 null 安全运算符 ?。

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

这样您就可以将可空类型传递给不可空类型参数