Search code examples
kotlinannotationsdelegates

How to add annotations to captured variables or delegated objects in Kotlin?


I want to dynamically implement an interface by extending an existing class as an anonymous object. This anonymous object captures a method parameter to implement the interface method:

fun myFunc(someObj: SomeObj, update: Boolean) = object : SomeObj(/*copy some values from someObj*/), SomeInterface {
   override fun doUpdate() = update
}

This implementation captures the update method parameter and adds it as a synthetic $update field into the anonymous object. I need to annotate this field as my serialization framework includes the $update field when its not marked as @Transient.

Another approach by delegation suffers from the same issue:

fun myFunc(someObj: SomeObj, update: Boolean) {
   val someInterfaceImpl = object : SomeInterface {
      override fun doUpdate() = update
   }
   return object : SomeObj(/*copy some values from someObj*/), SomeInterface by someInterfaceImpl 
}

I cannnot annotate someInterfaceImpl in any place with @delegate:Transient or @Transient. In essence:

  • Is there a way to annotate captured variables in Kotlin?
  • Is there a way to annotate the field when delegating to an object?

I am required to do this by annotations as the framework does not offer any other way to exclude fields, not even by names.

Furthermore I am not talking about delegated properties but delegated interfaces.


Solution

  • Create a named class:

    fun myFunc(someObj: SomeObj, update: Boolean): SomeObj {
        class SomeObjSubclass(someObj: SomeObj, @Transient val update: Boolean):
            SomeObj(someObj.prop1, someObj.prop2, /* and so on*/), SomeInterface {
            override fun doUpdate() = update
        }
        return SomeObjSubclass(someObj, update)
    }
    

    Notice that myFunc is now merely a wrapper for SomeObj. Depending on your design, you could just make myFunc the SomeObj subclass instead:

    class MyFunc(someObj: SomeObj, @Transient val update: Boolean):
            SomeObj(someObj.prop1, someObj.prop2, /* and so on*/), SomeInterface {
        override fun doUpdate() = update
    }
    

    Callers would call MyFunc(...) as if it were a function, and they would receive something assignable to SomeObj, just like before.

    You can also add a secondary constructor to SomeObj that takes a SomeObj, and copy the properties there

    constructor(someObj: SomeObj): this(
        someObj.prop1, someObj.prop2, /* and so on */
    )
    

    Then the declaration of MyFunc can just be:

    class MyFunc(someObj: SomeObj, @Transient val update: Boolean):
            SomeObj(someObj), SomeInterface {
        override fun doUpdate() = update
    }