在 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::plusAssign
和 Long::minusAssign
.
我无法让它工作。要么它不会编译,要么我在运行时得到 ClassCastException
s。
这是我要做的地方:
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 进行测试)。
我开始在 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::plusAssign
和 Long::minusAssign
.
我无法让它工作。要么它不会编译,要么我在运行时得到 ClassCastException
s。
这是我要做的地方:
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 进行测试)。