在 Kotlin 中将函数传递给函数

Passing functions to functions in Kotlin

我开始在 Kotlin 中使用 Vaadin,并创建了以下扩展方法:

fun AbstractComponentContainer.addButton(buttonText : String = "", icon : FontIcon? = null, function : (() -> Unit)? = null) {
    val button = Button(buttonText)
    if (icon != null) {
    button.icon = icon
    }
    if (function!=null) {
    button.addClickListener { function() }
    }
    this.addComponent(button)
}

这让我可以向容器添加一个按钮,带有一个可选的点击侦听器(和一个可选的图标)。

我现在想向组件添加两个按钮,一个用于向上,一个用于向下,以允许我在列表中上下移动项目。因此,我想调用 addButton() 两次。 lambda 中的逻辑将是相同的,唯一的区别是在一个项目的位置将递增而在另一个递减。

我正在尝试创建一个可以传递给 addClickListener 的函数,为了避免我编写两个做几乎相同事情的函数,我希望能够向该函数传递一个引用到 Long::plusAssignLong::minusAssign.

我无法让它工作。要么它不会编译,要么我在运行时得到 ClassCastExceptions。

这是我要做的地方:

val function = { function: (Long) -> Long ->
    val selectedItems: MutableSet<Item> = //get my items
    if (selectedItems.size == 1) {
        val elementAt = selectedItems.elementAt(0)
        elementAt.position = function(elementAt.position)
        Notification("Element's position is now ${elementAt.position}", "", Notification.Type.HUMANIZED_MESSAGE, true).show(Page.getCurrent())
        listDataProvider.refreshItem(elementAt)
    }
}
buttonLayout.addButton(buttonText = "Up", function = function(Long::inc) as (()->Unit)?)
buttonLayout.addButton(buttonText = "Down", function = function(Long::dec) as (()->Unit)?)

如果我删除演员表,它不会编译,如果我离开演员表,我得到以下内容:

java.lang.ClassCastException: kotlin.Unit cannot be cast to kotlin.jvm.functions.Function0

有没有办法实现我想要的?我真的不想更改 addButton.

期望的函数签名

(请注意,这是关于 Kotlin 的问题,而不是关于 Vaadin 的问题,因此我将关闭 Vaadin 标签)

您要的是currying,Kotlin不支持。解决方法是显式创建新的 lambda:

buttonLayout.addButton("Up", function = { function(Long::inc) })
buttonLayout.addButton("Down", function = { function(Long::dec) })

不赞同Voddan的解决方案,好像是把一个我们不该有的问题处理的招数

编译器告诉我 function 返回 Unit 而不是 () -> Unit。这似乎合乎逻辑,所以只需创建一个函数而不是 Unit:

val function = fun(longFunction: (Long) -> Long) = {
    val selectedItems: MutableSet<Item> = //get my items
    if (selectedItems.size == 1) {
        val elementAt = selectedItems.elementAt(0)
        elementAt.position = longFunction(elementAt.position)
        Notification("Element's position is now ${elementAt.position}", "", Notification.Type.HUMANIZED_MESSAGE, true).show(Page.getCurrent())
        listDataProvider.refreshItem(elementAt)
    }
}

这样你就可以简单地做到:

buttonLayout.addButton("Up", function = function(Long::inc))
buttonLayout.addButton("Down", function = function(Long::dec))

在某些情况下,出于可读性目的,我有时更喜欢 explicit/naive 解决方案(这是柯里化吗?):

val function = fun(longFunction: (Long) -> Long): () -> Unit {
    return fun() {
        val selectedItems: MutableSet<Item> = //get my items
        if (selectedItems.size == 1) {
            val elementAt = selectedItems.elementAt(0)
            elementAt.position = longFunction(elementAt.position)
            Notification("Element's position is now ${elementAt.position}", "", Notification.Type.HUMANIZED_MESSAGE, true).show(Page.getCurrent())
            listDataProvider.refreshItem(elementAt)
        }
    }
}

在我这边,它给出了相同的结果,而且我发现它阅读起来更自然,对于开始使用 Kotlin 的开发人员来说也更容易。我不知道这是否是一种柯里化,但它正在我这边编译和工作(通过模拟来自 Vaadin 的 class/event 进行测试)。