Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interface as functions in Kotlin

Tags:

android

kotlin

I am working on an android library that contains some views. Naturally these views can emit events.

I have an interface called (just for the purpose of this question) Listener. If I wrote the library in Java things would look like this:

public interface Listener {
    void onEvent();
}
public class SomeView extends FrameLayout {
    // Some more functions and implementation details

    public void setListener(Listener l) { ... }
}

When using this view in a Kotlin activity I can use the setListener like this:

someViewInstance.setListener {
    // implementation
}

I want to write my library in Kotlin, but it might be used in Java code as well, so I want to provide and interface for the listener just like a regular view (like above) but have the option for Kotlin code to use the function implementation:

interface Listener {
    fun onEvent()
}

when I try to use setListener like above in my Kotlin test activity I get a compilation error saying that the function expects type Listener but got () -> Unit.

Is there a way to enable this kind of implementation in Kotlin without having to create a new function for this?

I thought about having just one function that receives () -> Unit but then it look weird in the Java code (Function1 etc.).

Thanks!

like image 481
Dor Mesica Avatar asked Mar 12 '19 08:03

Dor Mesica


People also ask

How does Kotlin implement interface function?

Interfaces in Kotlin can contain declarations of abstract methods, as well as method implementations. What makes them different from abstract classes is that interfaces cannot store a state. They can have properties, but these need to be abstract or provide accessor implementations.

How do you call an interface in Kotlin?

In this example, we are implementing the interface MyInterface in InterfaceImp class. InterfaceImp class provides the implementation of property id and abstract method absMethod() declared in MyInterface interface. Output: Calling overriding id value = 101 MyInterface doing some work Implementing abstract method..

Are there interfaces in Kotlin?

Kotlin allows Interface to have code which means a class can implement an Interface, and inherit the behavior from it. After using Kotlin in Android development for a while, I've just realized the benefit of Interface in Kotlin.

How do you implement an interface in Kotlin example?

In the above example, we have created one interface named as “ExampleInterface” and inside that we have a couple of abstract properties and methods all together. Look at the function named “sayHello()”, which is an implemented method. In the following example, we will be implementing the above interface in a class.


2 Answers

You can define your interface as suggested and also add an extension that allows the usage of a lambda which is more idimatic for Kotlin code.

class SomeView {
    fun setListener(l: Listener) {}
}

fun SomeView.setListener(l: () -> Unit) = setListener(object : Listener {
    override fun onEvent() = l()
})

In Java, you would still be able to pass the Listener implementation.

like image 119
s1m0nw1 Avatar answered Sep 27 '22 18:09

s1m0nw1


This is called SAM-conversions,

Just like Java 8, Kotlin supports SAM conversions. This means that Kotlin function literals can be automatically converted into implementations of Java interfaces with a single non-default method, as long as the parameter types of the interface method match the parameter types of the Kotlin function.

But

Note that SAM conversions only work for interfaces, not for abstract classes, even if those also have just a single abstract method.
Also note that this feature works only for Java interop; since Kotlin has proper function types, automatic conversion of functions into implementations of Kotlin interfaces is unnecessary and therefore unsupported.

So, you can't write a simple Kotlin code to simulate this call.


In Java, if you write a

public interface Listener {
    void onEvent(); // SAM: Single Abstract Method. Only 1 is allowed
}

And you have a

public class SomeView extends FrameLayout {
    // skip the constructors

    public void setListener(Listener listener) {
        // do something
    }
}

Then you can do such a fancy call in Kotlin, thanks to SAM-conversion:

SomeView(this).setListener {} // asking a parameter with type () -> Unit for setListener
                              // Then parenthesis of function call can be omitted
// setListener function can also accept a parameter with type Listener
// by object : Listener {}

But if you convert that Java file into Kotlin, the code will report an error, due to the reason mentioned above. You have to implement a SomeView.setListener(() -> Unit) function by yourself, for example

fun SomeView.setListener(l: () -> Unit) {
    listener = object : Listener{
        override fun onEvent() {
            l()
        }
    }
}
like image 28
Geno Chen Avatar answered Sep 27 '22 19:09

Geno Chen