【问题标题】:Kotlin listeners that are Java- and Kotlin-friendly对 Java 和 Kotlin 友好的 Kotlin 侦听器
【发布时间】:2019-12-07 20:36:56
【问题描述】:

我看过类似的问答,但没有找到我要找的东西 - 也许我遗漏了一些东西。

我想(在 Kotlin 类中)实现一个监听器属性。监听器只有一个方法,所以它对 lambda 友好。

    interface Listener {
        fun onDone(id: String)
    }

...

    class Manager {
        var listener: Listener? = null
    }

当我想从 Java (8) 代码中设置这个属性时,我可以使用 lambda 干净地完成它:

    manager.setListener(id -> {

    });

但是,在 Kotlin 中,我必须使用方法创建一个匿名对象:

    manager.listener = object : OfflineManager.Listener {
        override fun onDone(id: String) {
        }
    }

另一个选项,在 Kotlin 中使用函数引用:

var listener: ((String) -> Unit)? = null

在 Kotlin 中允许使用不错的 lambda,但 Java lambda 需要一个返回值(即使该函数被定义为返回 Unit),这对 Java 开发人员来说很陌生。

那么我怎样才能兼顾两者呢?

【问题讨论】:

    标签: java kotlin interop


    【解决方案1】:

    我相信没有简单的方法可以定义这种在 Java 和 Kotlin 中“完全适用”的侦听器。如果你想在 Java 中使用你的监听器,你必须坚持一个接口,而 Kotlin 不支持 Kotlin 接口上的 SAM 转换:

    [...] 此功能 [SAM 转换] 仅适用于 Java 互操作;由于 Kotlin 具有适当的函数类型,因此不需要将函数自动转换为 Kotlin 接口的实现,因此不受支持。

    但是,您可以创建一个扩展方法(这样它在 Java 中不会作为 Manager 的方法之一可见),将给定的 lambda 转换为匿名对象:

    inline fun Manager.setListener(crossinline onDone: (String) -> Unit) {
        listener = object : Listener {
            override fun onDone(id: String) {
                onDone(id)
            }
        }
    }
    

    这样你就可以在 Java 和 Kotlin 中保持干净。


    编辑: 上述的变体:

    class Manager {
        private var listener: Listener? = null
    
        fun getListener(): Listener? = listener
    
        fun setListener(listener: Listener?) {
            this.listener = listener
        }
    }
    
    var Manager.onDone: (String) -> Unit
        get() = getListener()?.let { it::onDone } ?: {}
        set(value) {
            setListener(
                object : Manager.Listener {
                    override fun onDone(id: String) {
                        value(id)
                    }
                }
            )
        }
    

    在这种情况下,在 Java 中,您仍然可以使用 setter 和 getter(必须显式编写)引用侦听器,并且基于 lambda 的字段在 Java 的 Manager 实例中不可见。在 Kotlin 中,listener 变量在 Manager 类之外是不可见的,它可以作为 lambda 字段使用。

    但是,getListenersetListener 方法在 Kotlin 中都是可见和可访问的。 Kotlin 的 lambda 在被转换为匿名对象时也不会被内联。

    【讨论】:

    • 我不知道扩展方法对 Java 是隐藏的。但即使它们是,我如何对 Kotlin 隐藏基于接口的变体?
    • @noamtm:它们没有隐藏,Kotlin 为它们生成一个单独的 Java 类,并将它们作为静态方法放入其中。我的意思是在 Java 的 Manager 类实例中无法访问此扩展,因此 Java 开发人员不应该知道这种方法甚至存在。我不认为你可以在 Kotlin 的某个地方隐藏基于接口的变量。正如我所说,我觉得它相当干净,但它仍然只是一种解决方法。
    • @noamtm 我已经用第二种方法编辑了我的答案。
    【解决方案2】:

    很遗憾,Kotlin 中的接口不支持 SAM 转换。您可以阅读它,例如 here

    现在,您可以在 Java 中声明您的 Listener 接口:

    public interface Listener {
        void onDone(String id);
    }
    

    并在 Kotlin 中使用它

    manager.listener = Listener { println(it) }
    

    【讨论】:

      猜你喜欢
      • 2022-11-09
      • 2019-04-23
      • 2018-05-13
      • 1970-01-01
      • 2021-03-03
      • 1970-01-01
      • 1970-01-01
      • 2016-10-30
      • 1970-01-01
      相关资源
      最近更新 更多