在 Kotlin lambda 表达式中覆盖多个接口方法

Posted

技术标签:

【中文标题】在 Kotlin lambda 表达式中覆盖多个接口方法【英文标题】:Overriding multiple interface methods in Kotlin lambda expressions 【发布时间】:2018-03-14 01:52:01 【问题描述】:

假设我有一个Callbacks 接口,有两个方法onCurrentLocationonError

    public interface Callbacks 

        void onCurrentLocation(ClientLocation location);

        void onError();
    

以及在其构造函数中采用此接口的类,例如:

public MyLocationClient(Callbacks callbacks) ...

现在,在 Kotlin 中,我是否可以通过这种方式实例化 MyLocationClient:

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

如果不是,为什么不呢?

我看到的行为是:当接口只有一个方法时,这个对象的构造编译得很好。但是,一旦我向Callbacks 添加更多方法,编译器就会抱怨

"类型不匹配。必需:回调!找到:(ClientLocation) -> 单位”

编辑:删除了对location 的空检查,因为它与问题无关。

【问题讨论】:

【参考方案1】:

所以你在一个不是函数接口的匿名类上创建一个实例(它们只有一个方法)所以它会是这样的:

val myLocationClient = MyLocationClient(object : Callbacks 

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

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

【讨论】:

是的,这段代码运行良好,但我试图避免使用 kotlin lambdas 的“样板”代码。事实上,这就是我的问题的要点。 我解释了为什么,你没有功能接口,这就是它在 Kotlin 中的编写方式 - 在同一匿名类中的方法实现之间使用 , 在 Kotlin 中无效。如果您想创建更少的样板,将其拆分为 2 个功能接口并让构造函数接受 2 个参数,那么您的实现将起作用,或者只是在您的接口中删除 onError(),因为无论如何您都有任何实现 哦,对不起,我没查什么是函数式接口。这在 Kotlin 文档中的任何地方吗?【参考方案2】:

你可以定义

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

  override fun onError()  onErrorF() 

并使用它

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

它仍然有一些样板,但每个界面一次而不是每次使用一次,所以它很容易成为一个很好的权衡。

【讨论】:

【参考方案3】:

我想这是一件好事,它不起作用,因为当接口有两个相同类型的函数时,你会怎么做?

public interface Callbacks 
    void anotherFunction(ClientLocation location);

    void onCurrentLocation(ClientLocation location);

因此,我认为将其限制为 SAM(单一抽象方法)接口是一种好方法。

【讨论】:

我正在考虑将此作为公认的答案,因为据我了解,这实际上是您无法按照我的要求做的原因。【参考方案4】:
interface IAnimatedRatingBar 
   fun setProgressImageResource(resourceId: Int)


   fun setProgressImageDrawable(drawable: Drawable)


   fun setSecondaryProgressImageResource(resourceId: Int)


   fun setSecondaryProgressImageDrawable(drawable: Drawable)

   fun startAnimate()

这对我来说很好。

【讨论】:

你能把你用来作为函数参数传递的语法包括进来吗?【参考方案5】:

您可以使用 Kotlin 的高阶函数并合并接口检查此示例的两种方法

import android.location.Location

interface Callbacks 

 fun onCurrentLocation(location: Location)
 
 fun onError()


现在定义一个实现回调接口的类

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")



在任何你喜欢的地方使用它

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

【讨论】:

以上是关于在 Kotlin lambda 表达式中覆盖多个接口方法的主要内容,如果未能解决你的问题,请参考以下文章

kotlin方法传入lambda表达式参数并调用invoke什么意思

kotlin lambda 表达式作为可选参数

Kotlin-lambda表达式和高阶函数操作符

Kotlin的Lambda表达式以及它们怎样简化Android开发(KAD 07)

《Kotlin核心编程》笔记:函数和Lambda表达式

Kotlin学习与实践 Lambda