对于Kotlin何时推断平台类型感到困惑

时间:2019-10-27 15:46:33

标签: generics kotlin android-livedata kotlin-reified-type-parameters

仔细研究以下示例代码使我意识到,我对Kotlin何时推断平台类型感到困惑:

inline fun <reified U, V> LiveData<U>.map(crossinline f: (U) -> V): LiveData<V> {
    val result = MediatorLiveData<V>()
    result.addSource(this) { u -> // u: U!
        if (u is U) {
            result.value = f(u)
        }
    }
    return result
}

val x = MutableLiveData<Int>()
val y = x.map { it + 1 }

x.value = 1 // ok
x.value = null // will eventually crash from doing null + 1

我的希望是,当mapf时,这种使用reified + crossinline的方法可以防止u函数调用null,因为它将检查{{1 }}。不过,有趣的是,null is Int越过了支票。

我以为我一定会误解了reified + crossinline的工作原理,但似乎真正引起争议的是类型推断:null似乎被推断为U,而不是Int!。例如,手动指定Int的类型参数可以使工作正常:

map

所以。为什么Kotlin推断上面的平台类型? (毕竟,val y = x.map<Int, Int> { it + 1 } 的推断类型是x,而不是MutableLiveData<Int>。)除了自己进行推断之外,还有没有办法不推断平台类型?

1 个答案:

答案 0 :(得分:1)

在您走到Kotlin复杂化之类的复杂化泛型之前,您应该问的第一个问题是:为什么我可以这样做?

val x = MutableLiveData<Int>()
x.value = null 

以及 的答案都基于MutableLiveData是用Java而不是Kotlin编写的事实。

如果您是用Kotlin编写的,则该课程:

class MyKotlinClass<T: Any> {
    lateinit var value: T
}

,然后尝试执行此操作:

val x = MyKotlinClass<Int>()
x.value = null

然后它将无法编译。那是因为您已将类型定义为不可为空的Any

但是想象一下用Java编写这样的泛型类,其中AnyObject,除了提供注释,您不能对任何东西真正地定义为可空性:

class ExampleJavaClass<T> {
    public ExampleJavaClass(T param) {

    }

    static ExampleJavaClass getStringInstance() {
        return new ExampleJavaClass<String>(null);
    }
}

允许。因此,要允许与Java完全兼容的互操作,Kotlin必须假定null可以。不幸的是,它是不可见的。

在有人生成Kotlin特定的LiveData实现之前,该实现将加强此效果,您必须处理null,或者您可以自己写一个this。我不一定会鼓励自己写书,否则可能会有不利后果。

在许多情况下,如果您拥有Java代码,则可以通过注释Java来定义可空性,例如@NonNull。但是,看起来您无法使用类型参数上的现有注释来完成此操作。