kotlingenericsdelegateskotlin-delegate

Delegating function variables in Kotlin with generic class


I'm currently working on an application that requires delegating function variables. I've encountered some problems when trying to do so. Here's the code of a minimal reproducible example:

package org.prismsus.tank

import kotlin.reflect.KProperty
fun addFunc(a : Int, b : Int) : Int {
    return a + b
}

class Test{
    inner class PrintFuncCall1{
        operator fun getValue(thisRef: Any?, property: KProperty<*>): (Int, Int) -> Int {
            fun temp(a : Int, b : Int) : Int {
                println("Function ${property.name}")
                return addFunc(a, b)
            }
            return ::temp
        }
    }
    val delegatedFun1 : (Int, Int) -> Int by PrintFuncCall1()


    inner class PrintFuncCall2<F : (Array<out Any>) -> R, R>{
        operator fun getValue(thisRef : Any?, property : KProperty<*>) : (Array<out Any>) -> R{
            fun temp(vararg params : Any) : R{
                println("Function ${property.name}")
                println("Params: ${params.joinToString()}")
                return addFunc(params[0] as Int, params[1] as Int) as R
            }
            return ::temp
        }
    }

    val delegatedFun2 : (Int, Int) -> Int by PrintFuncCall2()
}

fun main(){
    val test = Test()
    println(test.delegatedFun1(1, 2))
    println(test.delegatedFun2(3, 4))
}

Here delegatedFun1 and PrintFuncCall1 worked fine. However, I tried to modify the delegate class to a generic class and found that this didn't work. Specifically, I got this compilation error for delegatedFun2 and PrintFuncCall2:

Property delegate must have a 'getValue(Test, KProperty<*>)' method. None of the following functions is suitable: 
public final operator fun getValue(thisRef: Any?, property: KProperty<*>): (Array<out Any>) -> ??? defined in org.prismsus.tank.Test.PrintFuncCall2

I don't quite get why getValue requires thisRef to be of type Test, which is the type of the larger class. This is especially strange as I'm delegating a variable of type (Int, Int) -> Int. Further, even if I tried to change the signature of getValue to getValue(thisRef : Test, property : KProperty<*>), I got the following error:

Property delegate must have a 'getValue(Test, KProperty<*>)' method. None of the following functions is suitable: 
public final operator fun getValue(thisRef: Test, property: KProperty<*>): (Array<out Any>) -> ??? defined in org.prismsus.tank.Test.PrintFuncCall2

Besides these, a minor question I have is about Kotlin's constrain mechanism to type parameters. My intention for PrintFuncCall2 is to mark F as the type of the function, and R as the return value of function F. However, when changing the signature of getValue from getValue(thisRef : Test, property : KProperty<*>) : (Array<out Any>) -> R to getValue(thisRef : Test, property : KProperty<*>) : F, I got this message from IDE on the line return ::temp:

Required:
KFunction1<Array<out Any>, R>
Found:
F

Isn't that these two supposed to be the same thing?


Solution

  • You issue seems to be a type mismatch between the expected return type of the delegate and the type you are giving it on the property level.

    Your delegate is supposed to return a function of type (Array<Any>) -> R but you are annotating the property with (Int, Int) -> Int. Array<out Any> is equivalent to vararg Any, but it does NOT mean "any function with any sort of parameters".

    The error should go away by annotating the property like this:

    val delegatedFun2: (Array<Int>) -> Int by PrintFuncCall2()
    

    Unfortunately there's no way to annotate a type as being a function with any sort or number of parameters. You will need to use an array.