What if I am only interested in onSee
and do not care for other events? Can I at least omit the methods that have no return values?
interface EventHandler
{
fun onSee()
fun onHear()
fun onSmell()
fun onTouch()
fun onAwake()
fun onSleep()
}
fun addEventHandler(handler:EventHandler)
{
}
fun Main()
{
addEventHandler(object:EventHandler
{
override fun onSee()
{
print("I see.")
}
})
}
Sure you can only implement one interface method, all you have to do is to provide a default implementation for the other methods in the interface declaration
interface EventHandler {
fun onSee()
fun onHear() { /* default implementation */ }
fun onSmell(){ /* default implementation */ }
fun onTouch(){ /* default implementation */ }
fun onAwake(){ /* default implementation */ }
fun onSleep(){ /* default implementation */ }
}
Now when you create an instance of this interface you only need to provide a compulsory implementation for onSee()
method, rest are optional
If you're not the author of the original interface
You could extend the original interface and provide a default implementation for the methods you want
interface OnSeeEventHandler: EventHandler {
override fun onHear() { /* default implementation */ }
override fun onSmell(){ /* default implementation */ }
override fun onTouch(){ /* default implementation */ }
override fun onAwake(){ /* default implementation */ }
override fun onSleep(){ /* default implementation */ }
}
And use the OnSeeEventHandler
to provide only onSee
method imeplementation
I came up with the following, somewhat p̶e̶r̶v̶e̶r̶s̶e̶ interesting approach.
The function below uses a dynamic proxy to "materialize" an interface and patch it with only the needed methods. Methods that are not patched will just return null
or Unit
, depending on the return type.
import java.lang.reflect.Proxy.newProxyInstance
inline fun <reified T> Any.materialize(): T = materialize(T::class.java, this)
fun <T> materialize(i: Class<T>, x: Any = object {}): T {
@Suppress("UNCHECKED_CAST")
return newProxyInstance(i.classLoader, arrayOf(i)) { _, m, args ->
x.javaClass.methods
.asSequence()
.filter {
it.name == m.name
&& it.parameterTypes!!.contentEquals(m.parameterTypes)
}
.map {
it.invoke(x, *args.orEmpty())
}.firstOrNull()
} as T
}
It can then be used as follow, given an interface Foo
and an anonymous object that contains only an implementation of its qux()
function:
interface Foo {
fun bar()
fun baz(): String
fun qux(s: String): String
}
fun main(vararg args: String) {
val foo = object {
fun qux(s: String): String {
return "Returned from qux: $s"
}
}.materialize<Foo>()
println(foo.bar()) // void no-op, prints "kotlin.Unit"
println(foo.baz()) // no-op with return value, prints "null"
println(foo.qux("Hello")) // prints "Returned from qux: Hello"
}
Disclaimer
- Using this, you lose all compile-time checking as everything is resolved at runtime.
- Some things are not covered by this implementation (e.g. interface default methods).
- Performance is not taken into account at all.
- Requires the
kotlin-reflect
dependency.
- Today is my second day learning Kotlin, so there might be any number of unaddressed edge cases and bugs.
I would not use this myself in most
cases, and will continue to hold out for a Kotlin construct that supports partial
interface implementations (like TypeScript's Partial<T>
).
I'm only providing
this approach because it might be of interest for some use cases, and I'm
sorry if this made your eyes bleed.