命名参数的省略括号将顺序颠倒

时间:2018-10-16 09:36:07

标签: groovy named-parameters

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”?

1 个答案:

答案 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有助于理解参数的顺序:

enter image description here

现在,如果我仅设置Map fragile,它将警告我们的方法调用有问题:

enter image description here

此外,使用@groovy.transform.TypeChecked@groovy.transform.CompileStatic批注有助于在编译时捕获此类问题。希望对您有所帮助。