http://docs.groovy-lang.org/latest/html/documentation/#_named_arguments中没有任何东西可以解释这种行为。
def foo(String a,Map b) { println "a: ${a}; b: ${b}" }
foo('a',b : 'c')
导致错误:No signature of method: Script1.foo() is applicable for argument types: (java.util.LinkedHashMap, java.lang.String) values: [[b:c], a]
def foo(String a,Map b) { println "a: ${a}; b: ${b}" }
foo('a',[b : 'c'])
打印出:a: a; b: [b:c]
在定义中交换参数的顺序也会使其编译:
def foo(Map b,String a) { println "a: ${a}; b: ${b}" }
foo('a',b : 'c')
打印出a: a; b: [b:c]
这是groovy中的错误,还是某种意想不到的“ groovy good”?
答案 0 :(得分:4)
这实际上是未记录的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
批注有助于在编译时捕获此类问题。希望对您有所帮助。