Hey all, question about reflection, I am trying to...
# announcements
a
Hey all, question about reflection, I am trying to do something like this but I am inexperienced with Kotlin and even brand new to reflection.
Copy code
class Example(val id: Int) {
    companion object {
        fun <T, V> demo(property: KProperty1<T, V>, value: V): V? {
            return null
        }
    }
}

// this works
val test1 = Example.demo(Example::id, 0)

// this works, but shouldn't pass validation
val test2 = Example.demo(Example::id, "1")
The idea is that I want to be able to infer the type of the passed-in property (in this case I would expect
Int
. I feel like I'm close but not understanding something. Any help (or even suggestions of what to Google) would be awesome.
l
Reflection tends to require runtime checks. I would look into property.returnType or javaField or something similar at runtime.
a
Hm, that would still mean that I can't infer the type and have IntelliJ provide a warning that the provided property type is invalid though right?
l
right
a
So like, the return type is correct in this case, but I would still have expected
"1"
to provide an error.
l
You could use the <> syntax to define T and V, but you would have to do that for each call
a
While certainly not out of the question, it's not my first choice for a solution. I feel like I'm so close to what my intent is here. More so because the return type is even correct.
I think this might be a winning alternative to experiment with. Specifying the parameter types directly (thanks for the passed in generics
<>
values comment, that made me start thinking about specifying them and led to this idea. Not sure how it will do but I'm okay with the results because it stops the build rather than relying on runtime exception throwing.
s
Maybe try reify V and use
when is
construct. Not sure if it will work.
a
@Sourabh Rawat where would
when is
be placed? I'm not sure were it's intended to go.
Or do you mean something along the lines of
Copy code
when (value) {
    is Int -> handleInt(value)
    else -> error("Unsupported value type: ${value::class}")
}
s
Yes
y
The real issue here is that test2 becomes of type Any? because that's the common ancestor between Int coming from id and String coming from "1"
The solution here is to use the
@kotlin.internal.Exact
annotation to simply limit the parameters from causing the type param to be Any?
like this basically: (playground)
Copy code
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
import kotlin.reflect.*
class Example(val id: Int) {
    companion object {
        fun <T, V> demo(property: KProperty1<T, @kotlin.internal.Exact V>, value: @kotlin.internal.Exact V): V? {
            return null
        }
    }
}
fun main() {
    // this works
    val test1 = Example.demo(Example::id, 0)
    // this works, but shouldn't pass validation
    val test2 = Example.demo(Example::id, "1")
}
🙌 1
a
Woah, this is awesome. I'm surprised I can access the
@kotlin.internal.Exact
annotation. Is that a byproduct of the suppression? It works interestingly well but I'd be a bit hesitant to use it because it seems like this is a bug though and the behavior could break at any point ?
Did a bit of digging, it totally is. Had no idea the extent of what could be suppressed.
y
It is a byproduct of the suppression, but tbh this specific suppression is lazy on my part. It's not really recommended to suppress such errors, and specifically in this case there's actually another solution that doesn't require suppression. Simply, the way the Kotlin type-checker knows that you're using the
Exact
annotation is simply by its package and name, and so you can use this trick to shadow the annotation, and this shadowing is much more reliable and "supported", if you will, than suppressing the error.
And I just realised that, in fact, the trick to shadow the annotation was posted as a reply on a Kotlin Discussions post that wanted to do pretty much what you're doing here 🤦. The poster was trying to do this, which looks a lot like your code:
Copy code
open class Foo(var barInt: Int)

// public interface KProperty1<T, out R> : KProperty<R>, (T) -> R

fun <E : Foo, T> test(field: kotlin.reflect.KProperty1<E, T>, value: T) {}

fun main(args: Array<String>) {
    test(Foo::barInt, 12345) // ok
    test(Foo::barInt, "NotInt") // ok! Type T is Any, but I would like compile error
}