Can I omit the interface methods I do not use in K

2019-02-16 08:03发布

问题:

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

回答1:

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



回答2:

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.



标签: kotlin