How to add annotations to captured variables or delegated objects in Kotlin? - 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.

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
}

Related

Default value for generic member

I'm trying this:
class Foo<T> {
var member: T = T()
}
...but the Kotlin compiler gives me an error: Type parameter T cannot be called as function.
How do I default-construct a generic member variable?
Well, to access the type information, we need to use the reified keyword on the type, but this is only applicable in inlined functions. So instead of relying on direct construction, a workaround can be to use a generator function wrapped in the companion object that immediately sets the member right after construction
// Test class to verify the implementation
class Yolo {
override fun toString() = "Yolo swag"
}
class Foo<T : Any> {
lateinit var member: T
companion object {
inline fun <reified T : Any> newInstance() =
T::class.java.newInstance().let { memberInstance ->
Foo<T>().apply { member = memberInstance}
}
}
}
fun main() {
// generate a Foo<Yolo>
val foo = Foo.newInstance<Yolo>()
println(foo.member) // displays "Yolo swag"
}
It's implied that T has a public no-arg constructor, but in general case it may not be true. This code uses reflection to bypass compiler complains about it (which may end up with runtime error if you dissapoint the JVM expectations and indeed pass T without public no-arg constructor).
//Reified generics at class level are not yet supported in Kotlin (KT-33213),
// so you have to pass instance of `KClass` manually as a consructor parameter
class Foo<T : Any>(clazz: KClass<T>) {
var member: T = clazz.createInstance()
}

Kotlin class generics without duplication

Consider an abstract class:
abstract class PubSubSubscriber<T : Any>(private val topic: KClass<T>) : BackgroundFunction<PubSubMessage> {
abstract fun consume(payload: T)
override fun accept(message: PubSubMessage, context: Context) {
val json = String(Base64.getDecoder().decode(message.data.toByteArray()))
val payload = objectMapper.readValue(json, topic.java)
consume(payload)
}
}
And implementation:
class MySubscriber : PubSubSubscriber<Payload>(Payload::class) {
Is there a way to define such abstract class so that I don't have to repeat twice the Payload and Payload::class in the class definition?
Yes, with some reflection.
At construction time, we can extract the type parameter and assign it to a property that no longer needs to be given to the constructor:
abstract class PubSubSubscriber<T : Any> {
val topic: KClass<T> = extractTypeParam<T>(0).kotlin
private fun <X> extractTypeParam(paramIdx: Int): Class<X> {
require(PubSubSubscriber::class.java == javaClass.superclass) {
"PubSubSubscriber subclass $javaClass should directly extend PubSubSubscriber"
}
#Suppress("UNCHECKED_CAST")
return (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[paramIdx] as Class<X>
}
abstract fun consume(payload: T)
override fun accept(message: PubSubMessage, context: Context) {
val json = String(Base64.getDecoder().decode(message.data.toByteArray()))
val payload = objectMapper.readValue(json, topic.java)
consume(payload)
}
Note the following limitations:
A) this solution works only if MySubscriber directly extends from PubSubSubscriber. However, the given code can detect if that's not the case and warn about it (at runtime). In such cases, there are the following solutions:
MySubscriber falls back to providing a duplicate argument (essentially what you already had)
the direct superclass of MySubscriber can provide a similar detection mechanism
B) You call reflection code every time a MySubscriber instance is created. This may be too slow in certain contexts, but for many this is unproblematic.

Override getters in Kotlin?

So I have an abstract class Composition, which has two children: one is a Track, and one is an Album (which is a group of Tracks).
class Composition(val name: String, ...)
class Track(name: String): Composition(name)
class Album(name: String, val tracks: List<Track>): Composition(name)
So far, so good. Now, I have the duration that is added. It is abstract in Composition, so I can override it in the children:
abstract class Composition(...){
abstract fun getDuration(): Int
}
Now, I can add override the method in the Track, which takes it as a parameter:
class Track(..., private val duration: Int): Composition(...){
override fun getDuration() = duration
}
And finally, I make the Album, whose duration is the sum of the Tracks:
class Album(..., val tracks: List<Track>): Composition(...){
override fun getDuration() = tracks.sumBy { it.getDuration() }
}
It works as intended, but I do not understand why I cannot simply use tracks.sumBy { it.duration }, since in Kotlin properties are nothing more than getters and setters (I'm thinking about the getDuration in Composition).
I feel like I'm missing something, because if the same code was written in Java, I would be able to call composition.duration as a property -- so that makes me think that Kotlin allows it from Java code, but not from Kotlin code, which is sad.
An other example:
Let's say I have a class named Artist, who wrote multiple Compositions:
class Artist(
val nom: String,
private val _compositions: MutableList<Composition> = ArrayList()
) {
// HERE (I wrote the extension method List<E>.toImmutableList)
fun getCompositions() : List<Composition> = _compositions.toImmutableList()
}
This is standard in Java (exposing immutable versions of Collections via getters, so they are not modified) ; Kotlin doesn't recognize it though:
val artist = Artist("Mozart")
artist.getCompositions() // Legal
artist.compositions // Illegal
I thought about making this a property, but:
- If I choose the type List<E>, I can override the getter to return the immutable list, but I cannot use regular methods (add...) as the List is immutable
- If I choose the type MutableList<E>, I cannot override the getter to return ImmutableList (which is a subclass of List that I wrote, and is obviously not a subclass of MutableList).
There's a chance I'm doing something ridiculous while there is an easy solution, but right now I cannot find it.
In the end, my question is: Why aren't manually-written getters considered properties when written from Kotlin?
And, if I'm mistaking, What is the expected way of solving both of these patterns?
If you want to use it as property, you should use Kotlin-way to override getter.
For example:
abstract class Composition(...){
abstract val duration: Int
}
// You can use "override" in constructor
// val - is immutable property that has only getter so you can just
// remove private modifier to make possible get it.
class Track(..., override val duration: Int): Composition(...){
...
}
class Album(..., val tracks: List<Track>): Composition(...) {
override val duration: Int
get() = tracks.sumBy { it.duration }
}
Also there are may be case when you need mutable property that can be changed only inside of object. For this case you can declare mutable property with private setter:
class SomeClass(value: Int) {
var value: Int = value
private set
}
Read more in docs: https://kotlinlang.org/docs/reference/properties.html#getters-and-setters
You have to define duration as an abstract property and not as an abtract function (https://kotlinlang.org/docs/reference/properties.html#getters-and-setters):
abstract class Composition(val name: String) {
abstract val duration: Int
}
class Track(name: String, override val duration: Int): Composition(name)
class Album(name: String, val tracks: List<Track>): Composition(name) {
override val duration: Int
get() = tracks.sumBy { it.duration }
}
The getter/setter conversion as properties does only work for Java classes (https://kotlinlang.org/docs/reference/java-interop.html#getters-and-setters).

Kotlin lazy initialization in subclass

I'm trying to build a string with properties that are initialized in a subclass.
I read about lazy initialization but somehow this doesn't work as I expected.
abstract class SubProcessFullNameBuilder(technicalDomain: TechnicalDomainEnumeration) {
protected val moduleName = "td.${technicalDomain.value().toLowerCase()}.shared"
private val packageName by lazy { packageName() }
private val processName by lazy { processName() }
val processFullName: String = "$moduleName/$packageName.$processName"
protected abstract fun packageName(): String
protected abstract fun processName(): String
}
class WorkerFullNameBuilder(
private val jmsDirection: JmsDirectionEnumeration,
technicalDomain: TechnicalDomainEnumeration,
private val cdmCode: String) : SubProcessFullNameBuilder(technicalDomain) {
override fun packageName() = "$moduleName.workers.${jmsDirection.value().toLowerCase()}.${cdmCode.toLowerCase()}"
override fun processName() = "Worker"
}
Since I have overridden the packageName() and processName() properties, I would expect that on calling the packageName property it would use the implementation from the subclass.
But when I call the processFullName property, it throws a java.lang.NullPointerException.
val builder = WorkerFullNameBuilder(JmsDirectionEnumeration.ESB_IN, TechnicalDomainEnumeration.INFOR, "ccmd")
val name = builder.processFullName
How can I initialize the packageName and processName properties in a proper way?
This is a case of calling a non-final method in a constructor and thus accessing uninitialized variables.
This line is still evaluated eagerly, at the time when the base class is constructed:
val processFullName: String = "$moduleName/$packageName.$processName"
To get the values of the two lazy properties, this will make calls to the abstract methods, of which packageName() refers to jmsDirection and cdmCode to return its value - these properties are not initialized yet, because their values are set after the superclass constructor runs. Here's a simplified version of the subclass' constructor, decompiled back to Java:
public WorkerFullNameBuilder(#NotNull JmsDirectionEnumeration jmsDirection, #NotNull TechnicalDomainEnumeration technicalDomain, #NotNull String cdmCode) {
super(technicalDomain);
this.jmsDirection = jmsDirection;
this.cdmCode = cdmCode;
}
As a demonstration, if you don't refer to these, for example, if you return constants in both of the subclass methods, your code will actually run fine:
override fun packageName() = "foo"
override fun processName() = "Worker"
However, the solution you need here is most likely to make the processFullName property itself lazy instead of the two values it uses (which you're evaluating at constructor time right now anyway, so you're not making use of them being lazy). This means you don't even need those two as separate properties:
abstract class SubProcessFullNameBuilder(technicalDomain: TechnicalDomainEnumeration) {
protected val moduleName = "td.${technicalDomain.value().toLowerCase()}.shared"
val processFullName by lazy { "$moduleName/${packageName()}.${processName()}" }
protected abstract fun packageName(): String
protected abstract fun processName(): String
}

What is the correct way to add an abstract private getter and a public setter?

I have an interface where I want a property that can be modified innside the class, but not outside. I cannot use val because it needs to be mutable and the var keyword can not have a specified private setter since this is in an interface.
In Java I would have done this:
public <T> getMyProperty();
I can use the same approach in kotlin and just write the getter function dirrectly, but this does not seem like a kotlinlike approach. Is there a better way to achieve the same as this?
fun getMyProperty()
In Kotlin, you can actually override a val with a var, so, I think, what you want can be expressed as follows:
interface Iface {
val foo: Foo
}
class Impl : Iface {
override var foo: Foo
get() = TODO()
private set(value) { TODO() }
}
Or you can override the val with a property with a backing field and default accessors:
class ImplDefaultGetter : Iface {
override var foo: Foo = someFoo
private set
}
In both cases, the mutability and the presence of a private setter become an implementation detail of the classes and are not exposed through the interface.