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

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.

Related

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.
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
}

This in kotlin interface delegation

Is there any way to pass this when using interface delegation? This would enable nice composability - but I found no way to do this.
Means something like:
interface Foo {
}
class FooImpl(bar: Bar) : Foo {
}
class Bar: Foo by FooImpl(this) {
}
as long as FooImpl doesnt need a parameter like this it works - but it would be great to access the other class there - perhaps someone knows a way. Otherwise I would also be interested if this is worth a KEEP if not - or if it will be impossible for some reason.
Delegation doesn't support this. The delegate has to be instantiated before the class that is delegating to it, so the delegate cannot rely on it for construction. Another gotcha is that although you can override functions of the delegate, if the delegate internally calls those functions, it calls the original version, not the override. The delegate really lives in its own world.
But you could set it up for the host to pass itself to the delegate in its initialization block:
interface Foo<T> {
var host: T
fun doSomething()
}
class FooImpl : Foo<Bar> {
override lateinit var host: Bar
override fun doSomething() {
println(host.name)
}
}
class Bar(val name: String): Foo<Bar> by FooImpl() {
init {
host = this
}
}
fun main() {
val bar = Bar("Hello world")
bar.doSomething()
}
This would unfortunately expose the host to the possibility of getting disconnected from its own delegate by outside classes, though. Maybe you could make the property throw an exception if assigned more than once.
Here's a property delegate that could do that:
private class SingleAssignmentVar<T>: ReadWriteProperty<Any, T> {
private var value: T? = null
private var assigned: Boolean = false
#Suppress("UNCHECKED_CAST")
override fun getValue(thisRef: Any, property: KProperty<*>): T {
if (!assigned)
error("Property has not yet been set.")
return value as T
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
if (assigned)
error("Property may only be set once.")
assigned = true
this.value = value
}
}
fun <T> Delegates.singleAssignment(): ReadWriteProperty<Any, T> = SingleAssignmentVar()
You may split your Bar class in two parts, say backend and frontend.
Frontend will be responsible for declaring interface with delegates, backend will host delegates and act as composition target.
For example:
interface Foo {
fun sayHello(): String
}
class FooImpl(val bar: BarBackend) : Foo {
override fun sayHello() = "Hello from ${bar.compositionTarget()}!"
}
class BarBackend() {
val fooImpl: FooImpl = FooImpl(this)
fun compositionTarget() = "backend"
}
class BarFrontend(backend: BarBackend) : Foo by backend.fooImpl
fun main() {
val bar = BarFrontend(BarBackend())
println(bar.sayHello())
}

How to get both static and instance access to a classes field

Suppose I have an abstract superclass A. That class has a property abstract val predicate: (ModelClass) -> Boolean.
Let B be a subclass.
I want to be able to do both of the following:
Use the predicate from an instance aInstance.predicate
Also use that predicate elsewhere, without having to create an instance to get that predicate B.predicate
How can I do this.
I don't think this is possible.
There is no such thing as an abstract static method in Kotlin or Java.
Perhaps this will give more insight.
Does your class need to be abstract? Maybe the code below can be useful:
open class MyClass {
companion object myCompanion {
val myStatic = "MyClass"
}
open val myStatic = myCompanion.myStatic
}
class MySubClass : MyClass() {
companion object myCompanionSubClass {
val myStatic = "MySubClass"
}
override var myStatic = myCompanionSubClass.myStatic
}
fun main() {
println(MyClass.myStatic)
val t = MyClass()
println(t.myStatic)
println(MySubClass.myStatic)
val subClass = MySubClass()
println(subClass.myStatic)
}
In this code you can define a static property and use it from the class or any instance. It is also possible to override the property in a subclass and use it in the same way.

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
}