Home > Enterprise >  Why is KFunction2 not a denotable type in Kotlin?
Why is KFunction2 not a denotable type in Kotlin?

Time:06-09

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 a KFunction.
  • It cannot be KFunction either, because that would not allow the call move(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'."
  • Related