In Kotlin, interfaces can have companions. However, they are not part of the contract of the interface, that means it's not abstract and you don't have to override it/can't override it. Methods defined on the companion can't be overriden either, so the following code doesn't do what one expects it does:
interface SomeInterface { companion object { fun xxx() { println("Base method by SomeInterface") } } } class MyImpl : SomeInterface { fun SomeInterface.Companion.xxx() { println("Overriden static method by MyImpl") } } fun test(impl: SomeClass.SomeInterface) { with(impl) { SomeClass.SomeInterface.xxx() // surprise here! } }
This prints
...
Base method by SomeInterface
...
The corresponding bytecode piece in the test function is
GETSTATIC SomeClass$SomeInterface.Companion : LSomeClass$SomeInterface$Companion; INVOKEVIRTUAL SomeClass$SomeInterface$Companion.xxx ()V
Since the goal is to enforce the presence of a static method, it's likely that there is no base implementation at all, so no default implementation in the interface. In this case, we can only define a companion object in the interface without any methods. It's then possible to define interface methods for the companion object, just like fun SomeInterface.Companion.myMethod(). This is similar to include extension functions in an interface's contract, as described in my last blog post. Implementing interfaces would then need to implement this method. Here's a complete example on how to do this.
class SomeClass { interface SomeInterface { companion object fun SomeInterface.Companion.xxx() } class MyImpl : SomeInterface { override fun SomeInterface.Companion.xxx() { println("Overriden static method by MyImpl") } } class MyOtherImpl : SomeInterface { override fun SomeInterface.Companion.xxx() { println("Overriden static method by MyOtherImpl") } } companion object { fun test(impl: SomeClass.SomeInterface) { with(impl) { SomeClass.SomeInterface.xxx() } } } } fun main(args: Array<String>) { test(SomeClass.MyImpl()) test(SomeClass.MyOtherImpl()) }
It will print
...
Overriden static method by MyImpl
Overriden static method by MyOtherImpl
...
The corresponding bytecode of the test function now looks like this:
GETSTATIC SomeClass$SomeInterface.Companion : LSomeClass$SomeInterface$Companion; INVOKEINTERFACE SomeClass$SomeInterface.xxx (LSomeClass$SomeInterface$Companion;)V
So the old invokevirtual became an invokeinterface. This most likely comes with a small performance overhead. But hey, can't have everything.
This construct allows for factory-like constructor functions. Sadly, operator functions are not allowed to be declared on interfaces. That means, the constructor method has to be named create or something, leading effectively to an interface function signature of
fun SomeInterface.Companion.create() : T
and a usage like
with(impl) { println("Created ${SomeInterface.create().javaClass}") }
This eliminates the sense in all this for me, because I would then rather type
println("Created ${impl.create().javaClass}")
The only worth-it syntax that would satisfy me would be this - based on an invoke operator function defined in the interface, usable through the interfaces companion object.
println("Created ${SomeInterface().javaClass}") //Not valid
Okay, at least I tried :)
Keine Kommentare:
Kommentar veröffentlichen