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()
}
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:
First correct your reified type T to T: Any or you cannot access all of the extensions in Kotlin reflection including declaredMemberProperties
It is easiest to get to the field from a property reference to be sure you are actually talking about something that is really a property, so for each of declaredMemberProperties use javaField to do so.
Since javaField is a custom getter and could be nullable, it is saved to a local variable so smart casting will work later.
Then if this field has the same type as the delegation class you are looking for, you can then access the field.
But first you have to force the field's accessibility because it is a 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]
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
}
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.
Related
When you have a backing object that you would like to wrap and delegate all properties with the same delegate logic, is there some way to generically describe a class implementation with all the properties delegated the same way? Take the example below:
class Subject<T, U>(
private val prop : KMutableProperty0<U>
) {
operator fun getValue(caller: T, prop: KProperty<*>) : U = this.prop.get()
operator fun setValue(caller: T, prop: KProperty<*>, value : U) {
// dostuff
this.prop.set(value)
}
}
interface Test {
val prop1 : String
val prop2 : String
}
#Serializable
class TestImpl(
override var prop1: String,
override var prop2: String
) : Test
// I don't want to have to write this boilerplate every time I need to delegate logic from a serializeable data object
class Wrapper( val test : TestImpl) : Test {
override var prop1 by Subject(testImpl::prop1)
override var prop2 by Subject(testImpl::prop2)
}
Is there any language feature that would let me write something like this:
class GenericWrapper<T , U : T>(val impl : U) : T /* can't do this obviously */{
// for each property on T, overwrite the property and delegate accordingly
}
The only thing I can think of at this point is writing my own compiler plugin, but is this the only option?
We're trying to do some generic processing in kotlin. Basically, for a given class, we want to get the related Builder object. i.a. for any object that extends a GenericObject, we want a Builder of that Object.
interface Builder<T : GenericObject>
object ConcreteBuilder: Builder<ConcreteObject>
We'd need a function that will return ConcreteBuilder from ConcreteObject
Our current implementation is a Map:
val map = mapOf<KClass<out GenericObject>, Builder<out GenericObject>>(
ConcreteObject::class to ConcreteBuilder
)
Then we can get it with:
inline fun <reified T : GenericObject> transform(...): T {
val builder = map[T::class] as Builder<T>
...
However this isn't very nice as:
we need an explicit cast to Builder<T>
the map has no notion of T, a key and a value could be related to different types.
Is there any better way to achieve it?
A wrapper for the map could be:
class BuilderMap {
private val map = mutableMapOf<KClass<out GenericObject>, Builder<out GenericObject>>()
fun <T: GenericObject> put(key: KClass<T>, value: Builder<T>) {
map[key] = value
}
operator fun <T: GenericObject> get(key: KClass<T>): Builder<T> {
return map[key] as Builder<T>
}
}
This hides the ugliness, while not completely removing it.
To use:
val builderMap = BuilderMap()
builderMap.put(ConcreteObject::class, ConcreteBuilder)
builderMap.put(BetonObject::class, BetonBuilder)
// builderMap.put(BetonObject::class, ConcreteBuilder) – will not compile
val builder = builderMap[T::class]
Can we implement Rust like Traits and generic Traits using Kotlin Interfaces?
Also is there any way of using fn(&self) like construct in Kotlin class/interface default function implementations?
Can some examples be shown please?
Thanks
I don't know much about Rust, I'm referrring to these two videos as for what you're talking about, generic traits and &self explaination.
In kotlin you'd implement them using interfaces and classes as you've guessed.
An example of that is:
interface GenericTrait { // Same as traits
// <T:Any> just makes method to be called for non-null values, if you use <T>, you can pass null as well
fun <T: Any> method(value: T)
}
class TraitImpl : GenericTrait { // Same as structs
val isDisabled = Random.nextBoolean() // instance variable
// you can access instance parameter using the this or even not using it at all as in below
override fun <T: Any> method(value: T) {
println("Type of value is ${value::class}, and the value is $value. I am $isDisabled")
// or explicitly call ${this.isDisabled}, both are the same
}
}
fun main() {
TraitImpl().method("Hello")
TraitImpl().method(23)
TraitImpl().apply { // this: TraitImpl
method(23)
method(Unit)
}
}
Result:
Type of value is class kotlin.String, and the value is Hello. I am true
Type of value is class kotlin.Int, and the value is 23. I am true
Type of value is class kotlin.Int, and the value is 23. I am false
Type of value is class kotlin.Unit, and the value is kotlin.Unit. I am false
You can extract implementation outside if you want as an extension function just like you do in Rust.
interface GenericTrait {
val isDisabled: Boolean
}
class TraitImpl : GenericTrait {
override val isDisabled = Random.nextBoolean()
}
// define methods out of class declaration
fun <T: Any> GenericTrait.method(value: T) {
println("Type of value is ${value::class}, and the value is $value. I am $isDisabled")
}
I´m working on a code with generics and when I use an in I got a TypeMismatch when compiling.
The code is the following:
open class A
class B:A()
data class DataContainer(val a:String,
val b:A)
interface Repo<T:A>{
fun setParam(param:T)
fun getParam():T
}
abstract class RepoImp<T:A>:Repo<T>{
private lateinit var parameter:T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class BRepo:RepoImp<B>()
class Repo2(val repo: Repo<in A>){
fun process(b:DataContainer){
repo.setParam(b.b)
}
}
val repoB = BRepo()
val repo2 = Repo2(repoB)// Here I got: Type mismatch: inferred type is BRepo but Repo<in A> was expected
I also tried changing the attribute repo from Repo2 to Repo<*>
Since BRepo is a Repo<B>, it is not a Repo<in A>, (but it would satisfy Repo<out A>).
In other words, a Repo<in A> must be able to accept setParam(A()), but BRepo.setParam() can only accept a B or subclass of B.
Or to put it another way, BRepo is a Repo<B>, which is a tighter restriction on the type than Repo<A> when it comes to writing values (but looser restriction when reading values).
The reason class Repo2(val repo: Repo<*>) doesn't work is that Repo<*> is essentially a Repo<in Nothing/out A>. You can't call setParam() on a Repo<*> with any kind of object.
There's a design flaw in your code that you can't fix simply by changing Repo2's constructor signature. As it stands now, Repo2 needs to be able write A's to the object you pass to it, and a BRepo by definition does not support writing A's, only B's. You will need to make at least one of your class's definitions more flexible about types.
It might be easier to understand the covariance limitation with more common classes:
val stringList: MutableList<String> = ArrayList()
var anyList: MutableList<in Any> = ArrayList()
anyList.add(5) // ok
anyList = stringList // Compiler error.
// You wouldn't be able to call add(5) on an ArrayList<String>
Basically MutableList<String> is not a MutableList<in Any> the same way Repo<B> is not a Repo<in A>.
The Repo2 class expect to consume only type A, use Repo2<T : A>(val repo: Repo<in T>)
open class A
class B : A()
class C : A()
class D : A()
class BRepo : RepoImp<B>()
class CRepo : RepoImp<C>()
class DRepo : RepoImp<D>()
interface Repo<T : A> {
fun setParam(param: T)
fun getParam(): T
}
abstract class RepoImp<T : A> : Repo<T> {
private lateinit var parameter: T
override fun setParam(param: T) {
parameter = param
}
override fun getParam(): T {
return parameter
}
}
class Repo2<T : A>(val repo: Repo<in T>) {
fun process(b: DataContainer<T>) {
repo.setParam(b.b)
}
}
data class DataContainer<T : A>(
val a: String,
val b: T
)
fun main() {
val repoB = BRepo()
val repoC = CRepo()
val repoD = DRepo()
val repo2 = Repo2(repoB)
val repo3 = Repo2(repoC)
val repo4 = Repo2(repoD)
repo2.process(DataContainer("Process B type", B()))
repo3.process(DataContainer("Process C type", C()))
repo4.process(DataContainer("Process D type", D()))
println(repo2.repo.getParam())
println(repo3.repo.getParam())
println(repo4.repo.getParam())
}
Given an interface:
interface Countable
{
val count: Int
}
And an implementation/factory:
fun countable(counter: () -> Int): Countable = object : Countable
{
override val count: Int
get() = counter()
}
I can implement this using the class by delegation feature:
class CountableThing : Countable by countable({ 123 })
So that this snippet predictably outputs 123:
fun main()
{
val countableThing = CountableThing()
println(countableThing.count)
}
My question is, in the context of the delegate class, is there any way to get an instance of the delegating receiver?
In other words, can my delegate Countable implementation (the anonymous object defined in fun countable) see/access the receiver instance of the CountableThing class?
I tried this:
fun <T> countable(receiver: T, counter: () -> Int): Countable = object : Countable
{
// ...
}
class CountableThing : Countable by countable<CountableThing>(this, { 123 })
But that's not valid, because expectedly:
class CountableThing : Countable by countable<CountableThing>(this, { 123 })
/^^^^
'this' is not defined in this context
No it can't, delegate objects are just objects they don't even know if they're going to be used to implement an interface through delegation. However, you can consider using delegated properties which are used to delegate properties setters and getters implementations:
class Example {
var p: String by Delegate()
}
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
then you can use
val e = Example()
println(e.p)
which prints:
Example#33a17727, thank you for delegating ‘p’ to me!
As you can see, in your delegate implementation you can use thisRef which is a reference to the object whose property is been delegated.