1

I'm working with a third-party library that has a function which expects an argument of type KProperty1<T, V>, and casts it to an instance of CallableReference internally. I need to acquire a reference to a class member through reflection that is an instance of both KProperty1 and CallableReference.

The function in the third-party library looks something like this:

fun <T : Any, V> thirdPartyFunction(property: KProperty1<T, V>)  {
    val callableReference = (property as CallableReference)
}

Everything works when I call the function with a member reference obtained with the :: operator:

class ExampleClass(
    var exampleProperty: String,
)

// this works
thirdPartyFunction(ExampleClass::exampleProperty)

When I call the function with a value obtained through reflection, I get a ClassCastException:

val reflectedProperty = ExampleClass::class.memberProperties
    .find { it.name == "exampleProperty" } as KProperty1<ExampleClass, String>

// class kotlin.reflect.jvm.internal.KMutableProperty1Impl cannot be cast to class kotlin.jvm.internal.CallableReference
thirdPartyFunction(reflectedProperty)

I'm using Kotlin 1.9.21 on the JVM.

3
  • Well, the main problem here is the third-party library. It literally says: "we don't support properties acquired through KClass". It may be tricky or even impossible to use it as you need. Commented May 2, 2024 at 11:22
  • @broot Is this actually a common way of saying "we don't support properties acquired through KClass"? That's a weird restriction to impose... Commented May 2, 2024 at 12:25
  • @Sweeper I mean, they explicitly required a reference. Not just any property, but a reference specifically. You were able to workaround this and this is nice. Still, I think the main problem is in the design of the library. Ideally, they shouldn't make assumptions on which type of KProperty was provided. Commented May 2, 2024 at 12:46

1 Answer 1

0

You can basically do what the compiler does when it encounters a property reference like this - create an instance of PropertyReference1Impl.

The constructor takes 3 parameters

  • the class in which the property is declared
  • the property name
  • the JVM method signature of the getter

Optionally, you can give it an additional flags parameter indicating whether it is synthetic in the Java world, and/or whether it is declared at the top level (not in a class). You can also give it a receiver if it is an extension property.

Let's consider the simple case of:

class Foo {
    val x = 1
}

For Foo::x, you would write:

PropertyReference1Impl(Foo::class, "x", "getX()I")

(The Kotlin compiler would actually create a new class inheriting from PropertyReference1Impl and override its get method to return the property value directly, instead of using reflection as the default implementation does.)

Similar such classes exist for KProperty0, KProperty2, mutable properties, and functions. For an example of doing this with functions, see my answer here


You can write a method like this to handle this more generally:

inline fun <reified T, R> KProperty1<T, *>.asCallableReference(): KProperty1<T, R> {
    val getter = javaGetter ?: throw Exception("Must have a JVM getter for this to work!")
    return PropertyReference1Impl(
        T::class, name,
        getter.name + MethodType.methodType(getter.returnType)
            .toMethodDescriptorString()
    ) as KProperty1<T, R>
}

This assumes that the receiver came from the memberProperties collection of some KClass, and that it is not an extension property, and that it has a Java getter (not @JvmField) that takes no parameters. There are probably other assumptions being made here that aren't always true, but this should work for most properties.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.