仔细研究以下示例代码使我意识到,我对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
我的希望是,当map
为f
时,这种使用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>
。)除了自己进行推断之外,还有没有办法不推断平台类型?
答案 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编写这样的泛型类,其中Any
是Object
,除了提供注释,您不能对任何东西真正地定义为可空性:
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
。但是,看起来您无法使用类型参数上的现有注释来完成此操作。