Let's assume we have the following class in Kotlin (version 1.6.21, targeting JVM 17):
interface Cursor(
fun seekOrNext(target: String): Boolean
fun seekOrPrevious(target: String): Boolean
)
What I would like to do is to accept a function pointer to either one of those methods in a higher-order function:
fun moveCursorAndLog(cursor: Cursor, move: ???, target: String): Boolean {
println("Moving cursor with ${move.name} to '${target}'.")
return move(cursor, target)
}
The problem is: what's the type we have to insert for ???
- It cannot be
(Cursor, String) -> Boolean
because that could be any lambda, and we can only call.name
on aKFunction
. - It cannot be
KFunction
either, because that would not allow the callmove(cursor, target)
If we write this in the IDE:
val m = Cursor::seekOrNext
... then the type hint will tell us that it is KFunction2<Cursor, String, Boolean>
, which is exactly what we need. However, the following:
fun moveCursorAndLog(cursor: Cursor, move: KFunction2<Cursor, String, Boolean>, target: String): Boolean {
println("Moving cursor with ${move.name} to '${target}'.")
return move(cursor, target)
}
... doesn't compile because my IntelliJ cannot find the type KFunction2
anywhere (yes, I made sure that I have kotlin-reflect
on my classpath).
Which leads me to believe that, while KFunction2
is a valid type within the Kotlin type system, it is not "denoteable", i.e. we cannot declare a method parameter of this type.
Is that really the case, or am I missing something?
CodePudding user response:
Except you can denote KFunction2
? IntelliJ doesn't give you autocomplete, but that's just IntelliJ. Here's the bug report for that. KFunction2
is supposed to be in the kotlin.reflect
package.
Just do
import kotlin.reflect.KFunction2
Then you can do:
fun moveCursorAndLog(cursor: Cursor, move: KFunction2<Cursor, String, Boolean>, target: String): Boolean {
println("Moving cursor with ${move.name} to '${target}'.")
return move(cursor, target)
}
This compiles just fine for me.
Of course, just spelling the whole fully qualified name without the import is also fine.
That said, KFunctionX
are indeed kind of special. They are generated dynamically by the compiler, by BuiltInFictitiousFunctionClassFactory.kt
, which possibly explains why IntelliJ has the aforementioned bug.
CodePudding user response:
import kotlin.reflect.KFunction1
interface Cursor {
fun seekOrNext(target: String): Boolean
fun seekOrPrevious(target: String): Boolean
}
fun moveCursorAndLog(move: KFunction1<String, Boolean>, target: String): Boolean {
println("Moving cursor with ${move.name} to '${target}'.")
return move(target)
}
class CursorImpl : Cursor {
override fun seekOrNext(target: String): Boolean {
return true
}
override fun seekOrPrevious(target: String): Boolean {
return true
}
}
val cursorInstance = CursorImpl()
val move = cursorInstance::seekOrNext
moveCursorAndLog(move, "somewhere else")
// Output: "Moving cursor with seekOrNext to 'somewhere else'."