为什么 lambda 函数参数的类型在 kotlin 中带星号的泛型类型上是 Nothing?

why lambda function parameter's type is Nothing on generic type with asterisk in kotlin?

当我调用一些 api 时,我希望使用带有通用参数的多个回调。 所以我定义了 CallBackData class class CallBackData<T>(val func: (T?) -> Boolean, val params: T?) 它不是数据 class。因为它超级 class 其他回调。

并且我为多个回调定义了 Array> 变量。

val callbackDts : Array<CallBackData<*>> = arrayOf(
    CallBackData(::sampleCallback1, SomeClass(1)),
    CallBackData(::sampleCallback2, "hello"),
    CallBackData(::sampleCallback3, -1),
)

但是当我调用 func 时,它说错误

Type mismatch.
Required: Nothing?
Found: Any?

我不明白。为什么? it.params 类型 T 是否与 it.func(param(T)) 相同?正确的?为什么没有类型?为什么不一样?

这是完整代码

fun start(){
    val callbackDts : Array<CallBackData<*>> = arrayOf(
        CallBackData(::sampleCallback1, SomeClass(1)),
        CallBackData(::sampleCallback2, "hello"),
        CallBackData(::sampleCallback3, -1),
    )

    callApi(callbackDts)
}

fun callApi(callbacks : Array<CallBackData<*>>){
    callbacks.forEach{
        it.func(it.params)
    }
}


fun sampleCallback1(params: SomeClass?) : Boolean {
    println("sampleCallback1 ${params.toString()}")
    return true
}

fun sampleCallback2(params: String?) : Boolean {
    println("sampleCallback2 $params")
    return true
}

fun sampleCallback3(params: Int?) : Boolean {
    println("sampleCallback3 $params")
    return true
}

data class SomeClass(val i:Int)
class CallBackData<T>(val func : (T?) -> Boolean, val params: T?)

我试着转换成这样(使用 out 关键字),但同样失败。(Lambda 的参数类型是 Nothing?)

    fun start(){
        val callbackDts : Array<CallBackData<out Any?>> = arrayOf(
            CallBackData(::sampleCallback1, SomeClass(1)),
            CallBackData(::sampleCallback2, "hello"),
            CallBackData(::sampleCallback3, -1),
        )

        callApi(callbackDts)
    }

    fun callApi(callbacks : Array<CallBackData<out Any?>>){
        callbacks.forEach{
            it.func(it.params)
        }
    }


    fun sampleCallback1(params: SomeClass?) : Boolean {
        println("sampleCallback1 ${params.toString()}")
        return true
    }

    fun sampleCallback2(params: String?) : Boolean {
        println("sampleCallback2 $params")
        return true
    }

    fun sampleCallback3(params: Int?) : Boolean {
        println("sampleCallback3 $params")
        return true
    }

    data class SomeClass(val i:Int)
    class CallBackData<T>(val func : (T?) -> Boolean, val params: T?)

期待您的回复。谢谢!

你可以定义一个函数

fun <T> CallBackData<T>.call() = func(params)

然后callApi可以改成:

fun callApi(callbacks : Array<CallBackData<*>>){
    callbacks.forEach{ it.call() }
}

然后 Kotlin 没有问题推断 funcparams 的类型匹配每个 CallBackData.

不幸的是,一旦您将 CallbackData<T> 投影到 CallbackData<*>T 的类型信息就消失了。不再知道 it.func 采用与 it.params.

相同的类型

但是您确实知道它们在 CallBackData class 本身是同一类型,不是吗?所以你可以只添加一个 call 方法

class CallBackData<T>(val func : (T?) -> Boolean, var params: T?) {
    fun call() = func(params)
}

callbacks.forEach{
    it.call()
}

或者您可以重载 invoke 运算符:

operator fun invoke() = func(params)

然后您就可以直接it()

即使您无法控制CallBackData,您仍然可以添加扩展功能:

operator fun <T> CallBackData<T>.invoke() = func(params)

添加到其他答案:如果这是您定义 CallBackData 的唯一原因,那么您真的不需要这个 class。 Kotlin 已经支持闭包,所以我们不需要单独拦截函数和参数:

fun start(){
    val callbackDts = arrayOf<() -> Unit>(
        { sampleCallback1(SomeClass(1)) },
        { sampleCallback2("hello") },
        { sampleCallback3(-1) },
    )

    callApi(callbackDts)
}

fun callApi(callbacks : Array<() -> Unit>){
    callbacks.forEach{
        it()
    }
}