If you know you won't be overriding the function in any implementations of your interface, you can use extension functions as a nice workaround for this issue. Just put an extension function in the same file as the interface (and at the top level so other files can use it).
For example, what you're doing could be done this way:
interface Foo {
// presumably other stuff
}
fun Foo.bar(): String {
return "baz"
}
See the docs on extension functions for more information about them.
One "gotcha" worth noting:
We would like to emphasize that extension functions are dispatched statically, i.e. they are not virtual by receiver type. This means that the extension function being called is determined by the type of the expression on which the function is invoked, not by the type of the result of evaluating that expression at runtime.
Put simply, extension functions don't do what you might expect from regular polymorphism. What this means for this workaround is that the default function cannot be overridden like a regular function. If you try to override it, you'll get some weird behavior, because the "overridden" version will be called whenever you're dealing explicitly with the subclass, but the extension version will be called when you're dealing with the interface generically. For example:
interface MyInterface {
fun a()
}
fun MyInterface.b() {
println("MyInterface.b() default implementation")
}
class MyInterfaceImpl : MyInterface {
override fun a() {
println("MyInterfaceImpl.a()")
}
fun b() {
println("MyInterfaceImpl.b() \"overridden\" implementation")
}
}
fun main(args: Array<String>) {
val inst1: MyInterface = MyInterfaceImpl()
inst1.a()
inst1.b() // calls the "default" implementation
val inst2: MyInterfaceImpl = MyInterfaceImpl() // could also just do "val inst2 = MyInterfaceImpl()" (the type is inferred)
inst2.a()
inst2.b() // calls the "overridden" implementation
}