传递闭包(不带逗号或括号)作为 Groovy 中函数的最后一个参数

lan*_*ter 4 groovy

如何label以这种方式调用该函数并将其设置为最后一个参数的闭包?仅当我label使用括号调用但我不需要它们时,此代码才有效。是否可以在没有它们的情况下将闭包设置为功能?

label = { name, callback ->
    callback()
}

label "lbl" {    // not works
    println "call $it"
}

label ("lbl") {  // works
    println "call $it"
}
Run Code Online (Sandbox Code Playgroud)

Szy*_*iak 6

如果您不想使用括号,那么您必须拆分闭包参数,以告诉编译器您要label使用以下两个参数执行:

label "lbl", {    
    println "call $it"
}
Run Code Online (Sandbox Code Playgroud)

这个符号相当于:

label("lbl", {    
    println "call $it"
})
Run Code Online (Sandbox Code Playgroud)

Groovy 具有这种语法糖,当闭包是函数原型的最后一个参数时,它允许您在括号外传递闭包,这就是以下符号起作用的原因:

label ("lbl") {
    println "call $it"
} 
Run Code Online (Sandbox Code Playgroud)

但它仍然相当于前面提到的那些。

如何做出label "lbl" { }有效的陈述?

如果你真的想编译如下代码:

label "lbl" {    
    println "call $it"
}
Run Code Online (Sandbox Code Playgroud)

您需要注意一件事 - 这种符号相当于:

label("lbl" {    
    println "call $it"
})
Run Code Online (Sandbox Code Playgroud)

这意味着有一个label接受单个参数的函数- 从接受单个参数的函数返回的值lbl- 闭包。这些函数的实现可能如下所示:

def label = { value ->
  // do something with value
  println "The value accepted by label function is '${value}'"
}

def lbl = { callback ->
  callback?.call()
}

label "lbl" {
  println "call $it"
}
Run Code Online (Sandbox Code Playgroud)

它编译并将以下输出打印到控制台:

call null
The value accepted by label function is 'null'
Run Code Online (Sandbox Code Playgroud)

当然,正如您所看到的,您必须实现方法,lbl并且在大多数情况下这是非常有限的 - 您不能label使用不同于lbl. 您可以实现 Groovy 的methodMissing(name, args)函数,每当调用不存在的方法时都会调用该函数。考虑以下示例:

def methodMissing(String name, args) {

  if (args.length == 1 && args[0] instanceof Closure) {
    println "Running missing method '${name}'"
    return args[0].call()
  }

  throw new MissingMethodException(name, this.class, args)
}

def label = { value ->
  // do something with value
  println "The value accepted by label function is '${value}'"
}

def lbl = { callback ->
  callback?.call()
}

label "lbl" {
  println "call $it"
}

label "test" {
  println "nothing"
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,当label "test" { println "nothing" }被调用时,Groovy 会使用,missingMethod因为test运行时上下文中不存在方法。

call null
The value accepted by label function is 'null'
Running missing method 'test'
nothing
The value accepted by label function is 'null'
Run Code Online (Sandbox Code Playgroud)

但是,这并不意味着您可以将其视为从代码中去除逗号分隔符或括号的等价物。这是一个用 Groovy 编写的简单 DSL 的示例,它使用methodMissing机制来满足不存在的方法调用。最后一个示例实际上与以下示例相同:

label("test" {
  println "nothing"
})
Run Code Online (Sandbox Code Playgroud)

一个需要单个参数的函数,在这种情况下,我们将它作为test具有单个参数的方法调用的结果提供{ println "nothing" }

我强烈建议接受这样一个事实,即 Groovy 要求您使用 coma 分隔函数参数,并且不要使用此类 DSL 从代码中删除 coma 或括号。Groovy DSL 非常强大,当您决定在代码中使用此工具时,您必须知道自己在做什么。例如,想想如果你打电话会发生什么:

label "test" { println "something" }
Run Code Online (Sandbox Code Playgroud)

出于某种原因,一个方法定义为以下闭包:

def test = { int num -> num + 2 }
Run Code Online (Sandbox Code Playgroud)

存在于您的班级中。您将开始收到异常,因为methodMissing在这种情况下不再被调用。方法test存在,但它没有接受单个闭包的签名。