Java和Kotlin友好的Kotlin侦听器

Java和Kotlin友好的Kotlin侦听器,java,kotlin,interop,Java,Kotlin,Interop,我看过类似的问答,但没有找到我想要的——也许我错过了什么 我想(在Kotlin类中)实现一个侦听器属性。侦听器只有一个方法,因此它是lambda友好的 interface Listener { fun onDone(id: String) } ... class Manager { var listener: Listener? = null } 当我想从Java(8)代码中设置此属性时,我可以使用lambda干净地进行设置

我看过类似的问答,但没有找到我想要的——也许我错过了什么

我想(在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中使用nice lambda,但是Java lambda需要返回值(即使函数定义为返回单元),这对于Java开发人员来说是陌生的


那么,我如何才能充分利用这两者呢?

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

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

但是,您可以创建一个扩展方法(这样它就不会在Java中作为
管理器的方法之一可见),将给定的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的
管理器
实例中不可见。在Kotlin中,
listener
变量在
Manager
类之外不可见,可以将其用作lambda字段


但是,
getListener
setListener
方法在Kotlin中都是可见的,并且可以访问。此外,Kotlin的lambda在转换为匿名对象时不会内联。

不幸的是,Kotlin中的接口不支持SAM转换。例如,你可以读到它

现在,您可以用Java声明
侦听器
接口:

public interface Listener {
    void onDone(String id);
}
并在Kotlin中使用它

manager.listener = Listener { println(it) }

我不知道扩展方法对Java是隐藏的。但即使是这样,我如何从Kotlin隐藏基于接口的变体?@noamtm:它们不是隐藏的,Kotlin为它们生成一个单独的Java类,并将它们作为静态方法放在内部。我的意思是,这个扩展在Java的
管理器
类实例中是不可访问的,因此Java开发人员甚至不应该意识到这样的方法的存在。我不认为您可以将基于接口的变量隐藏在Kotlin的某个地方。正如我所说的,我发现它非常干净,但它仍然只是一个解决办法。@noamtm我用第二种方法编辑了我的答案。