I have a cache of objects which are either in RAM or serialised to the disc. Now I want to make a class a class which has the same interface as the cached objects and delegates method calls by either querying whether the requested object is in memory and the making a direct call to it or loading the object from the disc and invoking the method on the loaded object. On the first view kotlins interface delegation seems to be perfect for this kind of problem.
interface Component {
fun func()
}
class ComponentImpl: Component {
override fun func() {}
}
class VirtualComponent: Component by getComponent() {
companion object {
fun getComponent(): Component
= queryComponent() ?: loadComponent()
}
}
But the problem is that getComponent()
would be called when VirtualComponent
is created. What I need is that getComponent
would be called when a method like func
is invoked.
So at the moment I had to write:
class VirtualComponent: Component {
override fun func() {
getComponent().func()
}
}
I have three ideas for syntax for that feature in descending elegance:
-
delegate
keyword afterby
Delegates the interface to a delegate
Example:
class CallByNeed<T>(private val call: () -> T) {
operator fun getValue(thisRef: Any, property: KProperty<*>): T = call()
}
fun <T> callByNeed(block: () -> T) = CallByNeed(block)
class VirtualDelegate : Componenent by delegate callByNeed { getComponent }
Pros:
- Very general - You could use other delegates too
- Backward compatible
Cons: - bit clumsy, maybe it would have been good to make the above standard without the
delegate
keyword
- Braces:
Example:
class VirtualDelegate : Componenent by { getComponent }
Pros:
- Simple
- Does what you expect
Cons: - Breakes backwards compatibility with existing kotlin sources:
class Func: () -> Unit by { someCode() }
would not compile anymore
→ who used this - Looks odd with class body because of too many braces
-
lazy
keyword
Usable before the object you delegate to
Example:
class VirtualComponent by lazy getComponent ()
Pros:
- Does what I want
- Does not break backwards compatibility
Cons: - Not very general
- Bad name for the keyword because it does not describe what it does
Currently I’m in favor of the first one because it’s the most general.
I would love to hear from you what you think about and if you think that there are more use cases for interface
delegation to delegates.