【问题标题】:Omiting brackets for named arguments inverts order省略命名参数的括号会颠倒顺序
【发布时间】:2019-03-20 19:19:16
【问题描述】:

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

【问题讨论】:

    标签: groovy named-parameters


    【解决方案1】:

    这实际上是未记录的 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 注释有助于在编译时发现此类问题。希望对您有所帮助。

    【讨论】:

    • 感谢详细的解释!我唯一不明白的是:重点是什么?我没有看到机器人示例解决任何问题,当然没有什么“强大”。
    • 嗯,我很想知道它背后的原因。我只能猜测,要求Map 是命名参数用例中的第一个参数是在早期引入的,并且从未改变过。我检查了3.0.0-alpha-3 Groovy 版本,这种行为保持不变。修复它会破坏向后兼容性,这听起来是一种非常罕见的情况,所以很可能没有人打扰。
    • 顺便说一句,即将发布的 Groovy 2.5.4 版本将更新有关命名参数的文档 - github.com/apache/groovy/pull/812 感谢您报告此内容,也感谢您! :)
    • 太好了,谢谢。希望它可以节省一些人的头疼
    猜你喜欢
    • 2011-07-11
    • 2021-06-10
    • 2019-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多