How can I implement KFunctionN in Kotlin?

2019-07-14 22:18发布

问题:

If I define

class MyF : KFunction0<Int> {
    override val name: String = "f"
    override fun invoke() = 42

    override val annotations: List<Annotation>
        get() = TODO()
    ....
}

I can write

val f0: KFunction0<Int> = MyF()
assertEquals("f", f0.name)

but if I try

assertEquals(42, f0())

I get java.lang.ClassCastException: MyF cannot be cast to kotlin.jvm.functions.Function0

How can I define my own implementation of KFunction0?

I can't work with () -> Int because I need the name property.

I'm using Kotlin 1.3.21.

Additionally - it seems that I can run

val f02 = MyF()
assertEquals(42, f02())

My actual use case is Can I convert a Kotlin KFunction1 to a KFunction0 by applying the argument?

回答1:

It is definitely not a good idea to explicitly implement an internal class from Kotlin JVM. You do not have code completion for that classes in IntelliJ or Android Studio for a reason

You may use callable references instead, to make Kotlin compiler generate all necessary classes for you. https://kotlinlang.org/docs/reference/reflection.html#callable-references

    fun myfun() = 42
    val kFunction = ::myfun


    println(kFunction.name)
    println(kFunction())

The benefit - future version of Kotlin will unlikely to break that code (and may break you inheritor class)

Should you need a longer name for the function, you may declare it like that

fun `my function with long name`() = 43


回答2:

In Kotlin 1.2 I haven't found KFunction0<T> but only KFunction<T> and I was able to do what you wanted:

import kotlin.reflect.*
import kotlin.test.assertEquals

class MyKF : KFunction<Int>{ // In Kotlin 1.3 you can extend KFunction0<Int>

    override val annotations: List<Annotation>
        get() = listOf()
    override val isAbstract: Boolean
        get() = false
    override val isExternal: Boolean
        get() = false
    override val isFinal: Boolean
        get() = true
    override val isInfix: Boolean
        get() = false
    override val isInline: Boolean
        get() = false
    override val isOpen: Boolean
        get() = false
    override val isOperator: Boolean
        get() = false
    override val isSuspend: Boolean
        get() = false
    override val parameters: List<KParameter>
        get() = listOf()
    override val typeParameters: List<KTypeParameter>
        get() = listOf()

    /**
     * I am not sure how get proper return type. So... This KFunction will return kotlin.Number.
     */
    override val returnType: KType
        get() = Int::class.supertypes[0]
    override val visibility: KVisibility?
        get() = KVisibility.PUBLIC

    override fun call(vararg args: Any?): Int {
        return 0
    }

    override fun callBy(args: Map<KParameter, Any?>): Int {
        return 0
    }

    override val name: String
        get() = "f"

    // Sience Kotlin 1.3
    override fun invoke(): Int {
        return 0
    }
}

fun main(args: Array<String>) {
    val kf = MyKF()
    assertEquals("f", kf.name)
    assertEquals(0, kf.call())

    println(kf.returnType)
    println(kf.name)
    println(kf.call())
    pritnln(kf.invoke())
}

I will update Kotlin to 1.3 later and I'll complete my answer (if it works in Kotlin 1.3).

So where can be your problem? Maybe (just lucky guess) check type based on overriden methods like returnType?

Edit:

After migrating to Kotlin 1.3 I was able to extend KFunction0<Int> instead of KFunction<Int>. The only changes - my class has to also override invoke(): Int. Still works!

Edit2:

I'm not sure if I just didn't saw KFunction0 in my IDE or is not present in Kotlin below 1.3.