When should I use delegation in Kotlin? - kotlin

I have been going through multiple links (One, Two) and documentation regarding the delegate pattern and somewhat understand the advantage it brings in the form of "composition over inheritance". I can see how the inbuilt delegate properties (lazy, vetoable, map, observable) are useful; but having a hard time understanding 2 areas:
1. Why/When should I write a custom delegate for property? How is it better than overriding getter/setter of that property?
Comparing the 2 approaches:
private var withoutDelegate: String = ""
get() = DataHelper.getLatestData(::withoutDelegate.name)
set(value) {
DataHelper.setLatestData(value)
field = value
}
val withDelegate by StringDelegateProvider()
class StringDelegateProvider {
operator fun getValue(thisRef: String?, property: KProperty<*>): String {
return DataHelper.getLatestData(property.name)
}
}
2. At the class level, how is delegation better than traditional composition patterns?
Comparing the 2 approaches - composition without delegation seems much more concise:
interface Base {
fun print()
}
class BaseImpl1(val x: Int) : Base {
override fun print() { print(x) }
}
class BaseImpl2(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun clientFunctionWithDelegation() {
val i1 = BaseImpl1(10)
val i2 = BaseImpl2(10)
val b1 = Derived(i1)
val b2 = Derived(i2)
b1.print()
b2.print()
}
fun clientFunctionWithoutDelegation(){
//wihtout extending Base, we can still create multiple types of Base and use them conditionally.
val i1: Base = BaseImpl1(10)
val i2: Base = BaseImpl2(10)
i1.print()
i2.print()
}
Would appreciate if the community can share some use-cases where delegation can help.

1: You can reuse the delegate without having to override get and/or set each time. Example: the lazy delegate
2: let's say you want to create a MutableList that prints the list every time you mutate the list. You don't want to reimplement MutableList, you just want to override the functions mutating the list. So instead of manually delegating every call, you just say class PrintList<T>(original: MutableList<T>) by original and you just override the functions you care about

Related

Kotlin Generic auto conversion to "out"

In the below code if we use generic in base and then extend it in a diff interface, kotlin doesn't respect the generic of the base interface.
Why is that so?
In the base I have not used "in" or "out" but still the extended interface by default becomes "out".
interface FeaturedCardAdapterContract {
interface View {
fun onCreate()
}
interface SubPresenter<V : View> {
fun onBind(v: V)
}
}
interface FeaturedTestAdapterContract {
interface View : FeaturedCardAdapterContract.View
interface Presenter : FeaturedCardAdapterContract.SubPresenter<View>
}
fun main() {
val featureImpl1: FeaturedTestAdapterContract.Presenter = object : FeaturedTestAdapterContract.Presenter {
override fun onBind(v: FeaturedTestAdapterContract.View) {
}
}
val featureImpl2: FeaturedTestAdapterContract.Presenter = object : FeaturedTestAdapterContract.Presenter {
override fun onBind(v: FeaturedTestAdapterContract.View) {
}
}
//Works but i won't be able to consume it in onBind bcz kotlin assumed it as "out"
val interfaceArray: Array<FeaturedCardAdapterContract.SubPresenter<out FeaturedCardAdapterContract.View>> = arrayOf(featureImpl1, featureImpl2)
//Dosen't Work-bcz kotlin assumes the type of featureImpl1 is FeaturedCardAdapterContract.SubPresenter<out FeaturedCardAdapterContract.View> ,Why?
val interfaceArray: Array<FeaturedCardAdapterContract.SubPresenter<FeaturedCardAdapterContract.View>> = arrayOf(featureImpl1, featureImpl2)
//Works but,Same as 1st method
val interfaceArray: Array<FeaturedCardAdapterContract.SubPresenter<*>> = arrayOf(featureImpl1, featureImpl2)
for (featureImpl in interfaceArray) {
//Won't work bcz of "out"
featureImpl.onBind(object : FeaturedCardAdapterContract.View {
override fun onCreate() {
//
}
})
}
}
Rename the interfaces to Processor, Animal, and Dog, and you will see why the compiler is correct about the types and what you are trying to do doesn't make sense.
Here's the renaming:
interface Animal // FeaturedCardAdapterContract.View
interface Processor<A: Animal> { // FeaturedCardAdapterContract.SubPresenter<V>
fun process(animal: A) // onBind
}
interface Dog: Animal // FeaturedTestAdapterContract.View
interface DogProcessor: Processor<Dog> // FeaturedTestAdapterContract.Presenter
In main, you are creating an array of 2 DogProcessors:
val processorImpl1 = object: DogProcessor {
override fun process(animal: Dog) {
}
}
val processorImpl2 = object: DogProcessor {
override fun process(animal: Dog) {
}
}
val array = arrayOf(processorImpl1, processorImpl2)
Then you are trying to loop through it and have them each process an animal:
val array = arrayOf(processorImpl1, processorImpl2)
for (processor in array) {
processor.process(object: Animal {
})
}
This is obviously not going to work no matter how you change the type of array. The processors in the array process dogs specifically, not animals in general. You could simply make this work by just giving it dogs instead of animals, or in your case:
val interfaceArray = arrayOf(featureImpl1, featureImpl2)
for (featureImpl in interfaceArray) {
featureImpl.onBind(object : FeaturedTestAdapterContract.View {
override fun onCreate() {
//
}
})
}
Note that the type of the array can be changed to Array<Processor<out Animal>> - an array of processors that only produces animals. This is because a producer of dogs is a kind of producer of animals. See also: PECS. However, since you want to call process (onBind) here, you want the processor to take in, or consume an animal, not produce one. Therefore, Array<Processor<out Animal>> is not what you want.
Just to clarify, you have defined featureImpl1 as FeaturedTestAdapterContract.Presenter, so it's a FeaturedCardAdapterContract.SubPresenter<FeaturedTestAdapterContract.View>.
Note the "Test" view here, not the "Card" one. This is your own definition of Presenter - the View you use in the definition is a shortcut for the test view FeaturedTestAdapterContract.View, NOT the card one FeaturedCardAdapterContract.View:
val featureImpl1: FeaturedTestAdapterContract.Presenter = object : FeaturedTestAdapterContract.Presenter {
// only wants test views here
override fun onBind(v: FeaturedTestAdapterContract.View) {
}
Now check this part:
Won't work bcz of "out"
featureImpl.onBind(object : FeaturedCardAdapterContract.View {
//...
})
Let's forget about out for the moment. You have defined your featureImpl1 so it accepts to bind only to the specific FeaturedTestAdapterContract.View. But here you're trying to pass a card view FeaturedCardAdapterContract.View, which is NOT a test view. If this were allowed, the body of featureImpl1 would just fail because it is given objects that are NOT of type FeaturedTestAdapterContract.View, nor even subtypes of it.
//Works but i won't be able to consume it in onBind bcz kotlin assumed it as "out"
val interfaceArray: Array<FeaturedCardAdapterContract.SubPresenter<out FeaturedCardAdapterContract.View>> = arrayOf(featureImpl1, featureImpl2)
Kotlin didn't assume anything here, you're marking out yourself. But it's normal that you have to write it because of what I explained above.
We've just seen that featureImpl1 is a SubPresenter<FeaturedTestAdapterContract.View>. It cannot be assigned to a SubPresenter<FeaturedCardAdapterContract.View> (without out) because that would mean it would need to accept more types than it actually can.

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.

Pass different generic types to function

I have a function in Kotlin that can take a generic object as a parameter. The two objects are unrelated and do not share any base types. They both however implement the same functions. I would like to re-use those functions within my function. Something along these lines:
fun storeUser(datastore: Any) {
datastore.storeName("John")
}
// Call the function
val datastore1 = DataStore1()
storeUser(datastore1)
val datastore2 = DataStore2()
storeUser(datastore2)
Both the DataStore1 and DataStore2 have a function called "storeName". Is there a way in Kotlin to re-use this function in the storeUser function? I tried playing around with Generics but this does not seem possible.
The example code above is simple. In my real app, there are many more functions beside storeName. If I can't have a common function to store my data, I will need to create two separate functions and duplicate the storage for both. That kind of sucks.
I recommend using a common interface for both classes. If they are provided by a thid-party library, you could wrap them in your own classes and interface.
If you don't want to do that, you could just check the type of the parameter in the storeUser function:
fun storeUser(datastore: Any) {
when(datastore) {
is DataStore1 -> datastore.storeName("John")
is DataStore2 -> datastore.storeName("John")
else -> throw IllegalArgumentException()
}
}
But note that if you have another datastore in the future, you will need to add one more is clause to this function. That makes this code not very maintainable...
Better solution
If you create an interface Datastore:
interface Datastore {
fun storeName(name: String)
}
and the make your datastores implement it:
class Datastore1 : Datastore {
//Datastore1.storeName implementation
}
class Datastore2 : Datastore {
//Datastore2.storeName implementation
}
Then, you don't need to check the types in storeUser function. Just change its parameter type to Datastore:
fun storeUser(datastore: Datastore) {
datastore.storeName("John")
}
If Datastore1 and Datastore2 are provided by a third-party library, you can wrap them in your own classes and implement your Datastore interface:
class FirstDatastore : Datastore {
private val datastore = DataStore1()
override fun storeName(name: String) {
datastore.storeName(name)
}
}
class SecondDatastore : Datastore {
private val datastore = DataStore2()
override fun storeName(name: String) {
datastore.storeName(name)
}
}
So you can call your function using your classes:
val datastore1 = FirstDatastore()
storeUser(datastore1)
val datastore2 = SecondDatastore()
storeUser(datastore2)
As I said in the comment to the question, it would really be better to write a common interface for these classes. If that's not possible because the classes come from an external dependency, the second best thing to do would be to wrap the code as Héctor did.
Kotlin is a statically typed language, so unfortunately wrapping code like this results in a lot of duplication. If you didn't want to write a new wrapper for every new instance of the DataStore, you could use reflection to call it dynamically. This way you only have to write the definition once. However, you forego all the compile-time benefits of static checks, so it's not really a good idea. It was good to do as an exercise though. 😎
class WrappedDataStore<T : Any>(private val dataStore: T) {
private fun callDynamically(methodName: String, vararg args: Any?) {
val argTypes = args.map { it?.let { it::class.java} }.toTypedArray()
dataStore.javaClass
.getMethod(methodName, *argTypes)
.invoke(dataStore, *args)
}
fun storeName(name: String) = callDynamically("storeName", name)
}
fun <T : Any> storeUser(dataStore: WrappedDataStore<T>) =
dataStore.storeName("John")
fun main() {
val one = WrappedDataStore(DataStore1())
val two = WrappedDataStore(DataStore2())
one.storeName("foo")
two.storeName("bar")
storeUser(one)
storeUser(two)
}
class DataStore1 {
fun storeName(foo: String) = println("DataStore1 $foo")
}
class DataStore2 {
fun storeName(bar: String) = println("DataStore2 $bar")
}
Output:
DataStore1 foo
DataStore2 bar
DataStore1 John
DataStore2 John

Kotlin - Delegation chain

In Kotlin, is it possible to have a Delegation chain ?
To demonstrate what i'm trying to achieve here's the example (https://kotlinlang.org/docs/reference/delegation.html) in the kotlin doc modified :
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { println(x) }
}
class Derived(var b: Base, val someData: Float = 10f) : Base by b
class SecondDerived(var b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
val derived = Derived(b)
val secondDerived: Base = SecondDerived(derived)
secondDerived.print()// prints 10
if (secondDerived is Derived) println(secondDerived.someData) //here secondDerived is Derived == false
}
I would expect "secondDerived" to be of type "Derived" but the cast say it is not.
I suspect that in memory the secondDerived base is indeed of type Derived, but the compiler cannot see that. Is there any way to make the cast work ?
On the JVM, a class can have only a single superclass, and Kotlin's class delegation does not change it in any way. All it does is generate implementations of Base interface methods that delegate to the Derived instance. It does not affect is checks.

Is it possible to avoid code repetition when an object should return a modified copy of itself?

I'm currently writing some classes that represent symbolic mathematical expressions. All of these are immutable.
However, I found myself often repeating the same kind of structure, so I created an interface to avoid repetition, but find myself unable to avoid duplicating the "substituteInside" method (see below), which returns a copy of the object with components corresponding to "find" replaced with "replace".
This behavior is the same for all instances of this interface.
In my current solution, the interface requires implementing a method createOp which returns the modified copy of the object.
interface UnarySymbolicOp<InType : Any,
OutType : Any,
OpType : UnarySymbolicOp<InType,OutType,OpType>> :
Symbolic<OutType> {
// Arg may be a complex expression
val arg: Symbolic<InType>
fun createOp(mesh: Symbolic<InType>) : OpType
override val variables
get() = arg.variables
override fun <V : Any> substituteInside(find: Symbolic<V>, replace: Symbolic<V>): OpType {
return createOp(arg.substitute(find, replace))
}
}
The interface can then be implemented as follows: these classes represent the operation of getting some component of an expression.
data class GetX(override val arg: Symbolic<Vector3d>) : UnarySymbolicOp<Vector3d, Double, GetX> {
override fun createOp(mesh: Symbolic<Vector3d>) = GetX(arg)
override fun eval() = arg.eval().x
}
data class GetY(override val arg: Symbolic<Vector3d>) : UnarySymbolicOp<Vector3d, Double, GetY> {
override fun createOp(mesh: Symbolic<Vector3d>) = GetY(arg)
override fun eval() = arg.eval().y
}
data class GetZ(override val arg: Symbolic<Vector3d>) : UnarySymbolicOp<Vector3d, Double, GetZ> {
override fun createOp(mesh: Symbolic<Vector3d>) = GetZ(arg)
override fun eval() = arg.eval().z
}
This improves things as other methods returning a copy of the object can use that method and thus can live in the interface, but I still have to copy this method everywhere, while it basically always does the same thing.