Uniformly delegated properties in kotlin - kotlin

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?

Related

Type inference of class type parameter in abstract method

// Bars.kt
abstract class Bar
class BarToo(/* fields */) : Bar()
// Foos.kt
abstract class Foo<T : Bar> {
abstract fun foo(bar: T)
}
class FooToo : Foo<BarToo>() {
override fun foo(bar: BarToo) { /* */ }
}
// FoosBars.kt
private val foos = HashMap<String, Foo<out Bar>>()
fun <T : Foo<out Bar>> putFoo(name: String, foo: T) {
foos.putIfAbsent(name, foo)
}
fun doFoo(name: String, bar: Bar) {
val foo = foos[name] ?: return
// Error: Type mismatch: inferred type is Bar but Nothing was expected
// https://pl.kotl.in/TSp3eO_Tj
foo.foo(bar)
}
If I manually specify the bounds of T at the method's declaration, the error in doFoo is resolved, e.g.:
abstract class Foo /* ... */ {
abstract <T : Bar> fun foo(bar: T)
}
but obviously prevents the subclasses from using the type parameter from the class declaration.
Is this type of hierarchy possible in Kotlin, or should I better explain what I am trying to accomplish in order to avoid an XY problem?
Thanks!
You need to use in instead of out. This allows child classes to be used as follows:
foo.foo(bar)
foo.foo(BarToo()) // no compile error
Ref: this

Access the set of abstract properties on sealed sub classes (in kotlin)

I'm got a situation where I have a common property that must be defined on each of the subclasses of a sealed class.
I'd like the ability to be able to access the set/list of these values without 'duplicating' the list (by hard coding it)
Hopefully the below code conveys what I mean
sealed class S {
companion object {
// want to avoid typing: listOf("these", "values", please")
// instead grab it from the classes themselves
val properties = S::class.sealedSubclasses.map { /* What to do here? */ }
}
abstract val property: String
}
class A(val d: String) : S() {
override val property: String = "these"
}
class B(val e: String) : S() {
override val property: String = "values"
}
class C(val f: String) : S() {
override val property: String = "please"
}
I'm aware of fun <T : Any> KClass<T>.createInstance(): T from kotlin.reflect.full, but my constructors have non optional parameters.
You can create a createInstance(vararg) extension function for that:
fun <T : Any> KClass<T>.createInstance(vararg args: Any): T =
java.constructors.first().newInstance(*args) as T
S::class.sealedSubclasses.map { it.createInstance("the string") }

Accessing the delegating receiver with by delegation in Kotlin

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.

Reference outside the sealed class in Kotlin?

I'm trying to create a class that uses its own state to operate on the state of an external object that it holds a reference to. The external object can be of class A or B, which are similar, but not controlled by the author. So a sealed class is created to access their common attributes, per this earlier answer from #SimY4.
// *** DOES NOT COMPILE ***
class A { // foreign class whose structure is not modifiable
val prop get()= "some string made the Class-A way"
}
class B { // foreign class whose structure is not modifiable
val prop get()= "some string made the Class-B way"
}
data class ABTool (val obj:AB, val i:Int, val j:Int) {
// class that manipulates i and j and uses them to do
// things with AB's "common" attributes through the sealed class AB
sealed class AB { // substitute for a common interface
abstract val prop: String
abstract val addmagic: String
data class BoxA(val o:A) : AB() {
override val prop get()= o.prop
override val addmagic get() = prop + this#???.magic // HOW TO REFERENCE?
}
data class BoxB(val o:B) : AB() {
override val prop get()= o.prop
override val addmagic get() = this#???.magic + prop // HOW TO REFERENCE?
}
}
val magic get()= "magic: ${i*j}"
}
The problem now is that I've figured out I can't operate on the external object in the way I want, because a sealed class can't refer to its outer class members. Is there a better way to make this work, even if using a different approach (other than sealed class), while:
not changing foreign classes A or B;
respecting that A and B (and many others in the real case) are similar, so I'm trying to write one tool that calculates and adds magic to A and B with the same code base; and
noting that although the ABTool tools are the same, the way they are applied to add magic is slightly different in A vs. B, just as the to access the conceptually common elements of A and B may be different.
Any thoughts on this or a similar workaround? Maybe a more functional approach that I haven't conceived yet?
If ABTool being a sealed class is something you can give up, then here's a solution:
Replace sealed with inner abstract at the ABTool declaration;
Mark BoxA and BoxB as inner as well;
data class ABTool(val obj: AB, val i: Int, val j: Int) {
inner abstract class AB {
abstract val prop: String
abstract val addmagic: String
inner class BoxA(val o: A) : AB() {
override val prop get() = o.prop
override val addmagic get() = prop + magic
}
inner class BoxB(val o: B) : AB() {
override val prop get() = o.prop
override val addmagic get() = magic + prop
}
}
val magic get() = "magic: ${i * j}"
}
(alternatively, instead of marking AB as inner, move BoxA and BoxB out of it to the scope of ABTool)
An alternative would be to add an ABTool field to AB:
sealed class AB(val tool: ABTool) {
abstract val prop: String
abstract val addmagic: String
data class BoxA(val o:A, tool: ABTool) : AB(tool) {
override val prop get()= o.prop
override val addmagic get() = prop + tool.magic
}
data class BoxB(val o:B, tool: ABTool) : AB(tool) {
override val prop get()= o.prop
override val addmagic get() = tool.magic + prop
}
}
and pass this when creating it from ABTool. That's just what inner really does, after all.
In this specific case the field happens to be unused in AB itself and so you can remove it from there and make it val in BoxA and BoxB.

Kotlin: How can I get the delegation class of a member property?

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.