Search code examples
androidkotlingenericsreflectionvariadic-functions

kotlin reference function with vararg parameter and convert Array<out to List<out


I have asked the question based on this topic Base Question

So, i want to ask in advanced. With someone answered the question with Array and List

Class Test<T,V>{
   var functionPara :(()->T)? = null
   var recallFunctionWithFunction:( (Array<out T>) -> V)? = null

   constructor(value: ()->T, recallFunctionWithFunction:   (Array<out T>) -> V  ){
   this.functionPara = value
   this.recallFunctionWithFunction = recallFunctionWithFunction
}
inline fun <reified T, V> compose(crossinline f: (Array<out T>) -> V, vararg g: () -> T): () -> V {
val results = g.map { it() }
return { f(results.toTypedArray()) }
}

fun <T, V> compose(f: (List<out T>) -> V, vararg g: () -> T): () -> V {
val results = g.map { it() }
return { f(results) }
}
}
fun runCompose(){
compose(functionPara,recallFunctionWithFunction).invoke()
}

But i discovered that When i reference a function with vararg parameter like that

fun functionA(vararg :Observable<Any>):LiveData<Boolean>{
}
fun functionB():Observable<Any>{
}

When i do something like ::functionA the type of A will be Array<out Observable<Any>>->LiveData<Boolean> Therefore , when i do something like that

Test<Observable<Any>,LiveData<Boolean>>(::functionB,::functionA).runCompose()

Situation 1 If I use the compose function with accepting List type it will show type mismatch due to referencing ::functionA will return Array

Image1

Situation 2 If i use the compose function with accepting Array type it will show the error

Cannot use 'T' as reified type parameter. Use a class instead

Image2

In previous post , some answered me that convert the array to list. But how to convert reference function with vararg parameter with Originally Array<out to List <out ?? when i reference a function like ::function the type must be Array<out but i want to plug it into compose function. I must convert it. Can anyone help? i stuck in there long days. Hope someone can save me!!


Solution

  • You can't convert a list to a typed array because of types erasure. I think the easiest way is to use List everywhere.

    class Test<T, V>(val recallFunctionWithFunction: (List<T>) -> V, val functionPara: () -> T) {
    
        inline fun compose(crossinline f: (List<T>) -> V, vararg g: () -> T): () -> V {
            return { f(g.map { it.invoke() }) }
        }
    
        fun runCompose() {
            compose(recallFunctionWithFunction, functionPara).invoke()
        }
    }
    
    fun functionA(l: List<Observable<Any>>): LiveData<Boolean> {
    ...
    }
    fun functionB(): Observable<Any> {
    ...
    }
    

    Another solution

    class Test<T, V>(
        val recallFunctionWithFunction: (Array<out T>) -> V,
        val functionPara: () -> T,
        val clazz: Class<T>
    ) {
    
        companion object {
            inline fun <reified T, V> create(
                noinline recallFunctionWithFunction: (Array<out T>) -> V,
                noinline functionPara: () -> T
            ) = Test(recallFunctionWithFunction, functionPara, T::class.java)
        }
    
        inline fun compose(crossinline f: (Array<out T>) -> V, vararg g: () -> T): () -> V {
            return {
                @Suppress("UNCHECKED_CAST")
                val arr = java.lang.reflect.Array.newInstance(clazz, g.size) as Array<T>
                g.forEachIndexed { index, fg -> arr[index] = fg.invoke() }
                f(arr)
            }
        }
    
        fun runCompose() {
            compose(recallFunctionWithFunction, functionPara).invoke()
        }
    }
    
    ...
    val t = Test.create(::functionA, ::functionB)
    t.runCompose()