Overriding multiple interface methods in Kotlin lambda expressions

Viewed 3910

Say I have a Callbacks interface with two methods onCurrentLocation and onError:

    public interface Callbacks {

        void onCurrentLocation(ClientLocation location);

        void onError();
    }

and a class that takes this interface in its constructor, say:

public MyLocationClient(Callbacks callbacks) {...}

Now, in Kotlin, should I be able to instantiate MyLocationClient this way:

val myLocationClient = MyLocationClient(
                    { location: ClientLocation ->
                          updateClientLocation(location)
                    },
                    {})

and if not, why not?

The behavior I'm seeing is: When the interface only has one method, the construction of this object compiles fine. But, as soon as I add more methods to Callbacks, the compiler complains

"Type mismatch. Required: Callbacks! Found: (ClientLocation) -> Unit"

Edit: removed the null check for location since it was irrelevant to the question.

5 Answers

So you're creating an instance on an anonymous class that is not a functional interface (they only have one method) so it would be something like :

val myLocationClient = MyLocationClient(object : Callbacks {

        override fun onCurrentLocation(location : ClientLocation?){
            location?.run{ updateLocation(this) }
        }

        override fun onError(){ // should you not handle errors? }
    })

You can define

class CallbacksImpl(private val onCurrentLocationF: (ClientLocation) -> Unit, private val onErrorF: () -> Unit) : Callbacks {
  override fun onCurrentLocation(location : ClientLocation) { onCurrentLocationF(location) }

  override fun onError() { onErrorF() }
}

and use it

MyLocationClient(CallbacksImpl(
    { location -> updateClientLocation(location) },
    {}))

It still has some boilerplate, but once per interface instead of once per use, so it can easily be a good tradeoff.

I guess this is a good thing, that it doesn't work, because what do you do, when the interface has two functions of the same type?

public interface Callbacks {
    void anotherFunction(ClientLocation location);

    void onCurrentLocation(ClientLocation location);
}

So restricting this to SAM (single abstract method) interfaces is a good approach I would say.

interface IAnimatedRatingBar {
   fun setProgressImageResource(resourceId: Int)


   fun setProgressImageDrawable(drawable: Drawable)


   fun setSecondaryProgressImageResource(resourceId: Int)


   fun setSecondaryProgressImageDrawable(drawable: Drawable)

   fun startAnimate()
}

This works fine for me.

You can use higher order function of Kotlin and merge both method of interface check this example

import android.location.Location

interface Callbacks {

 fun onCurrentLocation(location: Location)
 
 fun onError()

}

Now define a class that implement Callbacks interface

import android.location.Location

class MyLocationClient(private val callbackListener: (isSuccess:Boolean, location: Location?, error:String) -> Unit): Callbacks {

override fun onCurrentLocation(location: Location) {
    callbackListener(true,location, "No Error")
}

override fun onError() {
    callbackListener(false,null, "Unable to get location")
}

}

Use it wherever you like

val myLocationClient = MyLocationClient { isSuccess, location, error ->
        if (isSuccess){
            // proceed with location
        }else{
            // error occured show it to user
        }
    }
Related