【发布时间】:2019-01-18 08:13:14
【问题描述】:
首先我只想指出我知道Force a null into non-nullable type 和Kotlin Generics and nullable Class type 但我认为这些问题与我的不同(如果我错了请纠正我)。
背景
我正在开发一个名为Awaitility 的库,简单地说,它旨在等待谓词评估为真。 Kotlin API 提供了一种编写表达式的方法,如下所示:
// Create a simple data class example
data class Data(var value: String)
// A fake repository that returns a possibly nullable instance of Data
interface DataRepository {
// Invoked from other thread
fun loadData() : Data?
}
val dataRepository = .. // Implementation of DataRepository
// Now Awaitility allows you to wait until the "value" in Data is equal to "Something"
val data : Data = await untilCallTo { dataRepository.loadData() } has {
value == "Something"
}
这是因为如果dataRepository.loadData() 返回null 则has 返回false,并且如果data 是null 则从不调用提供的接收器函数({ value == "Something" })。如果条件不满足,Awaitility 也会抛出异常,因此我们知道从表达式返回的内容类型为 Data(而不是 Data?),如示例中所示。 p>
has 函数是这样实现的:
infix fun <T> AwaitilityKtUntilFunCondition<T?>.has(pred: T.() -> Boolean) = factory.until(fn) { t: T? ->
if (t == null) {
false
} else {
pred(t)
}
} as T
AwaitilityKtUntilFunCondition 看起来像这样:
data class AwaitilityKtUntilFunCondition<T> internal constructor(internal val factory: ConditionFactory, internal val fn: () -> T?)
(如果需要,您也可以找到 ConditionFactory here)
虽然上面的示例在传递给untilCallTo 的 lambda 返回可空类型(Data?)时效果很好,但如果我们将其传递给不可空类型(即Data),则无法编译。例如,如果我们简单地将存储库修改为如下所示:
interface DataRepository {
// Invoked from other thread
fun loadData() : Data // Notice that loadData now returns a non-nullable type
}
如果我们然后尝试与前面示例中相同的等待表达式:
val data : Data = await untilCallTo { dataRepository.loadData() } has {
value == "Something"
}
我们会得到一个编译时错误:
Error:(160, 20) Kotlin: Type mismatch: inferred type is AwaitilityKtUntilFunCondition<Data> but AwaitilityKtUntilFunCondition<Data?> was expected
Error:(160, 68) Kotlin: Type inference failed. Please try to specify type arguments explicitly.
这是(当然)正确的!
问题
我想要做的是以某种方式修改has 方法以强制返回类型始终是作为参数传入的类型的不可为空等价物(可以为可空或不可为空)。我试图做这样的事情(这是行不通的):
infix fun <T, T2> AwaitilityKtUntilFunCondition<T>.has(pred: T2.() -> Boolean): T2
where T : Any?, // Any? is not required but added for clarity
T2 : T!! // This doesn't compile
= factory.until(fn) { t: T ->
if (t == null) {
false
} else {
pred(t as T2)
}
} as T2
由于T2 : T!!,这无法编译,但我希望它表明了我的意图。 IE。我想以某种方式将T2 定义为:
- 如果
T可以为空,则类型为T的不可为空等效项 - 如果
T是不可为空的类型,则与T相同
这在 Kotlin 中可行吗?
更新:
我在 Awaitility 项目中创建了一个名为 has-with-non-nullable-type 的分支,您会在其中看到我在文件 KotlinTest 中讨论的编译时错误。这就是我想要编译的。您可以使用以下方法克隆它:
$ git clone https://github.com/awaitility/awaitility.git
更新 2:
我添加了gist,我认为它在不使用任何依赖项的情况下演示了问题。
【问题讨论】: