interface Greeter {
fun hello(): String
fun greet() = hello()
}
open class Base {
open fun Greeter(): Greeter = object : Greeter {
override fun hello() = "Base hello"
}
}
class Derived : Base() {
override fun Greeter(): Greeter = object : Greeter by super.Greeter() {
override fun hello() = "Derived hello"
}
}
fun main() {
println(Derived().Greeter().greet())
}
Surely nobody here would be fooled into thinking that this will print Derived hello
. However if you imagine Greeter
being defined elsewhere and being part of a large code base, then the Derived
class on its own may not raise any red flags. And in general, someone may eventually (or inevitably) write a function that calls another in the same interface, causing subtle breakage.
If the intent was to print “Derived hello”, then one fix would be to replace Greeter with
interface Greeter {
fun hello(): String
}
fun Greeter.greet() = hello()
Depending on your taste, this may be considered either kludgy or elegant.
Some languages have a more direct way to express the intent of “Derived hello”. That way is to modify the singleton class of an object, or to replace a virtual table entry, or whatever the animal is in the language.
Considering the fragility of delegation, and considering that “Derived hello” is (more often than not) the more useful behavior anyway, I wonder how hard/easy it would be for kotlin to modify a virtual table (or whatever you call them).
I’m not under the illusion that will be taken very seriously, but here is the non-compiling proposed code:
class Greeter {
abstract fun hello(): String
fun greet() = hello()
}
open class Base {
open fun Greeter(): Greeter = object : Greeter() {
override fun hello() = "Base hello"
}
}
class Derived : Base() {
override fun Greeter(): Greeter = object : super.Greeter() {
override fun hello() = "Derived hello"
}
}
Some fake generated code – just to show what I mean – is
interface Greeter {
fun hello(): String = __vtbl.hello(this)
fun greet(): String = __vtbl.greet(this)
data class __Vtbl(
val hello: (Greeter) -> String,
val greet: (Greeter) -> String
)
val __vtbl: __Vtbl get() = Greeter.__vtbl
companion object {
val __vtbl = __Vtbl(
hello = { throw AssertionError("abstract call") },
greet = { greeter -> greeter.hello() }
)
}
}
open class Base {
open fun Greeter(): Greeter = object : Greeter {
override val __vtbl = Greeter.__vtbl.copy(
hello = { "Base hello" }
)
}
}
class Derived : Base() {
override fun Greeter() = super.Greeter().let { greeter ->
object : Greeter {
override val __vtbl = greeter.__vtbl.copy(
hello = { "Derived hello" }
)
}
}
}