使用kwargs和varargs定义Groovy闭包

时间:2017-07-25 04:22:21

标签: groovy closures variadic-functions

我遇到了Groovy函数和闭包之间的不一致,这让我难以理解。

我可以定义一个带有签名def foo(Map kwargs=[:], ... varargs){...}的函数,它几乎完全符合我的预期。它几乎需要我提供的每个命名和未命名的参数。它也不需要参数并且可以正常工作。

我找不到一种类似的方法来定义具有相同签名的闭包。作为快速演示,当我运行以下四行时:

def foo(Map kwargs=[:], ...args ){"kwargs=${kwargs.toString()} args=${args.toString()}"}
def bar = { Map kwargs=[:], ...args -> "kwargs=${kwargs.toString()} args=${args.toString()}"}
println foo(1:1,2:2)
println bar(1:1,2:2)

我得到以下输出:

> groovy test.groovy
kwargs=[1:1, 2:2] args=[]
Caught: groovy.lang.MissingMethodException: No signature of method: test$_run_closure1.call() is applicable for argument types: (java.util.LinkedHashMap) values: [[1:1, 2:2]]
Possible solutions: any(), any(), doCall([Ljava.lang.Object;), any(groovy.lang.Closure), each(groovy.lang.Closure), any(groovy.lang.Closure) groovy.lang.MissingMethodException: No signature of method: test$_run_closure1.call() is applicable for argument types: (java.util.LinkedHashMap) values: [[1:1, 2:2]]
Possible solutions: any(), any(), doCall([Ljava.lang.Object;), any(groovy.lang.Closure), each(groovy.lang.Closure), any(groovy.lang.Closure)
    at test.run(test.groovy:4)

使用Groovy Version: 1.8.6 JVM: 1.8.0_131 Vendor: Oracle Corporation OS: Linux

运行

我想编写一个可以接受任意数量的命名或未命名参数的闭包,包括根本没有参数。这可能吗?

2 个答案:

答案 0 :(得分:2)

研究

我已经玩了一些你的例子,我已经发现了这种情况下发生了什么。

def bar = { Map kwargs = [:], ... args -> "kwargs=${kwargs.toString()} args=${args.toString()}" }
bar.class.methods.findAll { it.name == 'doCall' }.each { println it }

这是你定义的闭包,这里是println的输出(我们迭代这个匿名类中定义的所有方法,只打印那些名为doCall的方法):

public java.lang.Object cls$_run_closure1.doCall(java.lang.Object[])
public java.lang.Object cls$_run_closure1.doCall(java.util.Map,java.lang.Object[])

这里我们只有两个可能的签名来调用闭包 - 我们缺少一个带有java.util.Map参数的签名来满足你的期望。

解决方案

如果我们做了一些小改动 - 将默认值应用于...args数组参数,例如:

def bar2 = { Map kwargs = [:], ... args = [] -> "kwargs=${kwargs.toString()} args=${args.toString()}" }
bar2.class.methods.findAll { it.name == 'doCall' }.each { println it }

然后这是println

的输出
public java.lang.Object cls$_run_closure4.doCall(java.util.Map)
public java.lang.Object cls$_run_closure4.doCall(java.util.Map,java.lang.Object[])
public java.lang.Object cls$_run_closure4.doCall()

正如您所看到的,有一个单java.util.Map的签名,您可以按预期调用此bar2闭包:

bar2(1:1,2:2)
  

kwargs = [1:1,2:2] args = []

bar2(1:1,2:2,3,4)
  

kwargs = [1:1,2:2] args = [3,4]

哦,顺便说一下 - 我用Groovy 2.4.8测试了它,我没有任何旧版本来测试它。如果它也适用于1.8.x,请告诉我们。

这是我必须承认的非常有趣的案例。如果这是一个bug,我也很好奇,或者这是为groovy的闭包设计的正确行为。也许有深入了解内部知识的人可以给我们一个提示:)

答案 1 :(得分:0)

可以使用MethodClosure作为解决方法:

def foo(Map kwargs=[:], ...args ){"kwargs=${kwargs.toString()} args=${args.toString()}"}
def cfoo = this.&foo
println cfoo.getClass()
println cfoo(1:1,2:2)

输出:

class org.codehaus.groovy.runtime.MethodClosure
kwargs=[1:1, 2:2] args=[]