如何在 Kotlin 中实现 KFunctionN?

3
最初的回答
如果我定义

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

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

最初的回答
我可以编写代码。
val f0: KFunction0<Int> = MyF()
assertEquals("f", f0.name)

最初的回答,但如果我尝试

assertEquals(42, f0())

我收到了一个异常:java.lang.ClassCastException: MyF cannot be cast to kotlin.jvm.functions.Function0

我该如何定义自己的KFunction0实现?

我无法使用() -> Int,因为我需要name属性。

我正在使用Kotlin 1.3.21。

此外,似乎我可以运行。

"最初的回答"

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

我的实际使用情况是如何通过应用参数将Kotlin KFunction1转换为KFunction0?


你使用哪个Kotlin版本? - Cililing
1
根据https://github.com/JetBrains/kotlin/blob/master/spec-docs/function-types.md,`kotlin.FunctionN`和`kotlin.reflect.KFunctionN`都是“虚构的,意味着它们没有源代码和运行时表示”。我预计您的使用情况需要这个缺失的运行时表示。 - Alexey Romanov
2个回答

4

明确从Kotlin JVM实现内部类绝对不是一个好主意。你在IntelliJ或Android Studio中没有那些类的代码完成功能,这是有原因的。

相反,您可以使用可调用引用,让Kotlin编译器为您生成所有必要的类。 https://kotlinlang.org/docs/reference/reflection.html#callable-references

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


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

好处 - Kotlin 的未来版本不太可能破坏该代码(但可能会破坏您的继承类)

如果您需要更长的函数名称,可以像这样声明

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

但是在你的例子中,kFunction 的类型是 KFunction0<Int>。我可以将其赋值给 () -> Int,但这样我就失去了 .name 属性,而这对我的使用情况来说是不可或缺的。 - Duncan McGregor
可以使用类型,是的,它是KFunction0<Int>。好处是你不需要在代码中手动实现该类型。 - Eugene Petrenko
我有一个使用案例,我想通过应用参数将KFunction1转换为KFunction0。我得出的结论是这是不可能做到的事情! - Duncan McGregor
部分应用是可能的。您可以仅声明一个用于部分应用的函数,并将其用于::引用。因此,名称将不是原始函数。另一种方法是创建自己的类/接口data class MyFunction1(val name: String, val f: (T) -> R) { operator fun invoke(t: T) = f(t) } data class MyFunction0(val name: String, val f: () -> R) { operator fun invoke() = f() }现在定义柯里化:fun MyFunction1.curry(t: T) = MyFunction0(name){ f(t) } (它也可以是成员函数) - Eugene Petrenko

1
在Kotlin 1.2中,我没有找到KFunction0<T>,只有KFunction<T>,我能够做到你想要的:
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())
}

我稍后会将Kotlin更新到1.3,并且我会完成我的回答(如果它在Kotlin 1.3中有效)。

那么你的问题可能出在哪里?也许(只是猜测)可以根据重写方法的类型检查,例如returnType

编辑:

迁移到Kotlin 1.3后,我能够扩展KFunction0<Int>而不是KFunction<Int>。唯一的更改-我的类还必须覆盖invoke(): Int。仍然有效!

编辑2:

我不确定是否在我的IDE中没有看到KFunction0还是在Kotlin 1.3以下不存在。


在你的代码中明确实现那个接口绝对不是最好的主意。你可以创建自己的接口,或者使用可调用引用来强制编译器为你完成这个任务。 - Eugene Petrenko
不对,Kotlin 1.3 中有 KFunction0。@DuncanMcGregor 我更新了我的回答。KFunction0 存在并且有 invoke 方法 ;) - Cililing
嗯,我不认为这是一个最好的创造代码的方法,但我只是回答问题而已 :P - Cililing
你试过运行最后一行代码吗?(在将其改为println之后) - Duncan McGregor
啊,更多信息。如果你指定 kf 的类型为 val kf: KFunction0<Int> = MyKF(),当你调用 invoke 时会出现 ClassCastException 错误,但是当你调用 call 时却不会。 - Duncan McGregor
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接