Using default function implementation of interface in Kotlin

Kotlin

Kotlin Problem Overview


I have a Kotlin interface with a default implementation, for instance:

interface Foo {
    fun bar(): String {
        return "baz"
    }
}

This would be okay until I try to implement this interface from Java. When I do, it says the class need to be marked as abstract or implement the method bar(). Also when I try to implement the method, I am unable to call super.bar().

Kotlin Solutions


Solution 1 - Kotlin

Generating true default methods callable from Java is an experimental feature of Kotlin 1.2.40.

You need to annotate the methods with the @JvmDefault annotation:

interface Foo {
    @JvmDefault
    fun bar(): String {
        return "baz"
    }
}

This feature is still disabled by default, you need to pass the -Xjvm-default=enable flag to the compiler for it to work. (If you need to do this in Gradle, see here).

It really is experimental, however. The blog post warns that both design and implementation may change in the future, and at least in my IDE, Java classes are still marked with errors for not implementing these methods, despite compiling and working fine.

Solution 2 - Kotlin

Please see the related issue.

There is a recommendation in the comments:

> Write your interface in Java (with default methods) and both the Java and Kotlin classes correctly use those defaults

Solution 3 - Kotlin

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
}

Solution 4 - Kotlin

Since Kotlin 1.4.0, you can use one of the following compiler flags:

  • -Xjvm-default=all
  • -Xjvm-default=all-compatibility (for binary compatibility with old Kotlin code)

This will enable JVM default method compilation for all interfaces.

If you want to read up on how to set these flags in your IDE or Maven/Gradle project, check out the documentation on compiler options.

Progress on this is being tracked in issue KT-4779, which also includes a helpful summary of the current state. The @JvmDefault annotation and the older -Xjvm-default=enable and -Xjvm-default=compatibility compiler flags should no longer be used.

Solution 5 - Kotlin

Unlike earlier version of Java8, Kotlin can have default implementation in interface.

When you implement Foo interface into a Java class. Kotlin hides those implementation of interface method. As stated here.

> Arrays are used with primitive datatypes on the Java platform to avoid the cost of boxing/unboxing operations. As Kotlin hides those implementation details, a workaround is required to interface with Java code

This is specific for Arrays in above link but it also applies to all the classes (May be to give support for earlier version of Java8).

EDIT

Above explanation is opinion based.

One thing i came across and that is the main reason.

>Kotlin binaries were compiled with java bytecode version 1.8 without default methods in interfaces. And they are facing critical issue solving it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVojtěchView Question on Stackoverflow
Solution 1 - Kotlinzsmb13View Answer on Stackoverflow
Solution 2 - KotlinKaratheodoryView Answer on Stackoverflow
Solution 3 - KotlindrmercerView Answer on Stackoverflow
Solution 4 - KotlinTordanikView Answer on Stackoverflow
Solution 5 - Kotlinchandil03View Answer on Stackoverflow