这实际上是未记录的 Groovy 行为。当使用带有附加参数的命名参数时,如果您在映射定义中跳过方括号,Groovy 期望代表命名参数的Map 参数是该方法的第一个参数。如果我们分析编译器生成的字节码,我们将看到以下行:
foo('a',b : 'c')
用以下Java代码表示:
CallSite[] var1 = $getCallSiteArray();
return var1[1].callCurrent(this, ScriptBytecodeAdapter.createMap(new Object[]{"b", "c"}), "a");
如您所见,传递给callCurrent() 方法的参数顺序与调用foo() 方法时定义的参数顺序相反。这有点令人困惑,尤其是添加方括号会显式更改生成的字节码:
foo('a', [b: 'c'])
用以下Java代码表示:
CallSite[] var1 = $getCallSiteArray();
return var1[1].callCurrent(this, "a", ScriptBytecodeAdapter.createMap(new Object[]{"b", "c"}));
它在 Venkat Subramanian 的“Programming Groovy 2”一书中得到了简要解释:
class Robot {
def type, height, width
def access(location, weight, fragile) {
println "Received fragile? $fragile, weight: $weight, loc: $location"
}
}
robot = new Robot(type: 'arm', width: 10, height: 10)
robot.access(x: 30, y: 20, z: 10, 50, true)
robot.access(50, true, x: 30, y: 20, z: 10)
“这个access() 方法接收三个参数,但是如果第一个参数是Map,我们可以在参数列表中围绕映射的键值浮动。(...)虽然那种灵活性Robot 的例子很强大,它可能会让人混淆,所以要谨慎使用它。(...)我们可以通过将第一个参数显式命名为 Map 来避免这样的混淆:“
def access(Map location, weight, fragile) { /* .. */ }
顺便说一句,像 IntelliJ IDEA 这样的 IDE 有助于理解参数的顺序:
现在,如果我只设置Map fragile,它会提醒我们的方法调用有问题:
此外,使用@groovy.transform.TypeChecked 和@groovy.transform.CompileStatic 注释有助于在编译时发现此类问题。希望对您有所帮助。