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

Time:03-29

when i call some api, i wished use multiple callback with generic parameter. so i defined CallBackData class class CallBackData<T>(val func: (T?) -> Boolean, val params: T?) it not data class. because it super class of other callbacks.

and i define Array<CallBackData<*>> variable for multiple callback.

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

but when i call func, it say error

Type mismatch.
Required: Nothing?
Found: Any?

i don't get it. why? isn't same it.params type T is same of it.func(param(T))? right? why is Nothing Type? why is not same?

this is full code

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?)

i tried convert to like this (using out keyword), but it's failed same.(Lambda's parameter type is 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?)

i look forward to your reply. thanks!

CodePudding user response:

Unfortunately, the type information of T is gone once you projected a CallbackData<T> to CallbackData<*>. It is no longer known that it.func takes the same type as it.params.

But you do know that they are the same type in the CallBackData class itself, don't you? So you can just add a call method

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

and

callbacks.forEach{
    it.call()
}

Or you can overload the invoke operator:

operator fun invoke() = func(params)

You would then be able to do it() directly.

Even if you don't have control over CallBackData, you can still add an extension function:

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

CodePudding user response:

Adding to other answers: if this is the only reason why you defined the CallBackData, then you don't really need this class. Kotlin has support for closures, so we don't need to intercept functions and parameters separately:

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

    callApi(callbackDts)
}

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

CodePudding user response:

You can define a function

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

and then callApi can be changed to:

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

Then Kotlin does not have a problem to infer that the types of func and params match for each CallBackData.

  • Related