【问题标题】:Define Groovy closure with kwargs and varargs使用 kwargs 和 varargs 定义 Groovy 闭包
【发布时间】:2017-12-30 19:48:23
【问题描述】:

我遇到了 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一起运行

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

【问题讨论】:

  • 警告,吹毛求疵:Groovy 没有命名参数——只有 Maps

标签: groovy closures variadic-functions


【解决方案1】:

调查

我对您的示例进行了一些尝试,并且发现了在这种情况下发生了什么。

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。

我必须承认这是一个非常有趣的案例。我也很好奇这是一个错误还是为 groovy 的闭包设计的正确行为。也许有深入常规内部知识的人可以给我们一个提示:)

【讨论】:

  • 如果我尝试bar2(1,2,3),我会得到另一个 MissingMethodException;这适用于foo 和bar。
【解决方案2】:

作为解决方法 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=[]

【讨论】:

    猜你喜欢
    • 2011-03-15
    • 1970-01-01
    • 1970-01-01
    • 2013-03-03
    • 2011-07-12
    • 1970-01-01
    • 2010-10-23
    • 2011-01-02
    • 1970-01-01
    相关资源
    最近更新 更多