我遇到了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
我想编写一个可以接受任意数量的命名或未命名参数的闭包,包括根本没有参数。这可能吗?
答案 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=[]