Reputation: 10290
How can I get the delegation class of a member property?
By this, I mean is it possible to complete such a function:
inline fun <reified T> delegationExample(t: T) {
for (prop in T::class.declaredMemberProperties) {
val delegatedClass = // what to do?!
}
}
Where the delegation class may look like:
class DelegationExample {
operator fun getValue(ref: Any, prop: KProperty<*>) = 0
}
And the declaration class might look like this:
object Example {
val a by DelegationExample()
val b by DelegationExample()
val c by DelegationExample()
}
Upvotes: 5
Views: 3156
Reputation: 516
One way to avoid reflection is to first initialize your delegate object and store it as a member of its own, and then delegate your property by it.
object Example {
val aDelegate = DelegationExample()
val bDelegate = DelegationExample()
val cDelegate = DelegationExample()
val a by aDelegate
val b by bDelegate
val c by cDelegate
}
Upvotes: 2
Reputation: 85946
To find properties that delegate to a delegate class along with the instance of that class, here is a utility function:
data class DelegatedProperty<T : Any, DELEGATE : Any>(val property: KProperty1<T, *>, val delegatingToInstance: DELEGATE)
inline fun <reified T : Any, DELEGATE : Any> findDelegatingPropertyInstances(instance: T, delegatingTo: KClass<DELEGATE>): List<DelegatedProperty<T, DELEGATE>> {
return T::class.declaredMemberProperties.map { prop ->
val javaField = prop.javaField
if (javaField != null && delegatingTo.java.isAssignableFrom(javaField.type)) {
javaField.isAccessible = true // is private, have to open that up
@Suppress("UNCHECKED_CAST")
val delegateInstance = javaField.get(instance) as DELEGATE
DelegatedProperty(prop, delegateInstance)
} else {
null
}
}.filterNotNull()
}
A few notes:
T
to T: Any
or you cannot access all of the extensions in Kotlin reflection including declaredMemberProperties
declaredMemberProperties
use javaField
to do so. javaField
is a custom getter and could be nullable, it is saved to a local variable so smart casting will work later. private
field. Running this in test program:
class DelegationExample {
operator fun getValue(ref: Any, prop: KProperty<*>) = 0
}
class Example {
val a by DelegationExample()
val b by DelegationExample()
val c by DelegationExample()
}
fun main(args: Array<String>) {
findDelegatingPropertyInstances(Example(), DelegationExample::class).forEach {
println("property '${it.property.name}' delegates to instance of [${it.delegatingToInstance}]")
}
}
The output is something like:
property 'a' delegates to instance of [DelegationExample@2c1b194a]
property 'b' delegates to instance of [DelegationExample@4dbb42b7]
property 'c' delegates to instance of [DelegationExample@66f57048]
Upvotes: 4
Reputation: 33769
On the byte code level delegated properties do not defer from regular ones (public getter/setter and a private field).
One way you could go is scanning the private fields of Example
and filtering those which have operator getValue(thisRef: R, KProperty<*>)
. Technically a field may contain a delegate object val x = lazy {1}
, but that is not very likely.
Upvotes: 1