Groovy 命名参数

Groovy 命名参数,groovy,named-parameters,Groovy,Named Parameters,我有办法 def test(String a, String b) { } 我想用一个动态参数映射来调用它。 我一直认为 test(['1','2']); //valid call 而且 test([a:'1',b:'2']); //=> does not work 会有用的。但事实并非如此。所以我想起来了,但没法用 有没有一种方法可以使用某种映射作为参数而不是单个参数来调用上述方法?也许我遗漏了什么,但我认为Groovy现在还没有命名参数。有,但我不知道有什么官方消息 对于你的情况

我有办法

def test(String a, String b) { }
我想用一个动态参数映射来调用它。 我一直认为

test(['1','2']); //valid call
而且

test([a:'1',b:'2']); //=> does not work
会有用的。但事实并非如此。所以我想起来了,但没法用


有没有一种方法可以使用某种映射作为参数而不是单个参数来调用上述方法?

也许我遗漏了什么,但我认为Groovy现在还没有命名参数。有,但我不知道有什么官方消息

对于你的情况,我认为地图的传播可能会有所帮助,但不是在每一种情况下。获取值后,它将按照声明映射值的顺序执行:

def test(String a, String b) { "a=$a, b=$b" }
def test(Map m) { test m*.value }

assert test(a: "aa", b:"bb") == "a=aa, b=bb"
assert test(b: "aa", a:"bb") != "a=aa, b=bb" // should be false :-(
assert test(b: "ccc", a:"ddd") == "a=ddd, b=ccc" // should have worked :-(
我可以建议你上课吗


方法调用不应该是
test(a:'1',b:'2')而不是
测试([a:'1',b:'2'])

请检查命名参数

thanx的评论,我找到了适合我问题的解决方案:

如果我定义一个没有类型的参数,我可以传入所有类型,包括hashMaps。groovy将类似
a:'h',b:'i'
的构造自动转换为hashmap

def test(myParams, Integer i) {
    return myParams.a + myParams.b
}

assert test(a:'h',b:'i',5) == test(b:'i',a:'h',5)
assert test([a:'h',b:'i'],5) == test(b:'i',a:'h',5)
test('h','i',5); //still throws an exception

这样,我可以使用单个命名参数,但也可以使用映射

命名参数支持非常灵活,但文档有点精简。以下是我发现的一些规则。 请注意,我试图在参数(在方法中声明)和参数(传递给方法调用)的用户中明确表示

  • 必须首先声明Map参数first。 这是最大的一个。而且不明显
  • 在参数中不需要完整的映射,只需映射元素即可 i、 e.
    (a:“aa”)
    已经足够好了,您不需要
    ([a:aa])
  • 您可以将有序(未命名)参数与名称参数混合使用,只要 因为有序参数的顺序与参数的顺序相同 他们填补
  • 您可以将命名的arg与常规有序的arg穿插在一起。 这很酷,但订购的args必须在, 好吧,点餐
  • 您甚至可以在同一系统中使用可选的有序参数 方法签名(参见下面示例中的
    x
  • 您可以为Map参数指定一个默认的空Map
    args=[:]
    making 命名参数是可选的,但是如果您有 其他可选参数(参见下面最后的示例)
以下是一些例子: 参数不需要键入,但为了清晰起见,我添加了类型

//此方法有一个映射参数来捕获所有命名的参数
//和非命名(有序)参数字符串s、int n和int x
//x有一个默认值,因此是可选的
//捕获命名参数的映射(此处为非类型映射)必须首先出现
defm(映射参数=[:],字符串s,整数n,整数x=1)
{
println“s:$sn:$nx:$x,args:$args”
}
//1:首先传入命名参数,然后进行排序
m(a:aa,b:3,ss,44,5)//s:sn:44x:5,args:[a:aa,b:3]
//2:排序的args first-命名的args last(相同结果)
m(“ss”,44,5,a:“aa”,b:3)//s:sn:44x:5,args:[a:aa,b:3]
//3:将第一个订购的参数带到开始位置(相同结果)
m(“ss”,a:“aa”,b:3,44,5)//s:sn:44x:5,args:[a:aa,b:3]
// 4:在命名ARG的中间粘贴有序的ARG n(同样的结果!)
m(“ss”,a:“aa”,44,b:3,5)//s:sn:44x:5,args:[a:aa,b:3]
//5:将已排序的参数与已命名的参数混合,并使用默认值(x=1)跳过参数x
m(a:aa,“ss”,b:3,44)//s:ssn:44x:1,args:[a:aa,b:3]
//6:先订购arg n-因此顺序错误(失败!)
//m(44,“ss”,a:“aa”,b:3,5)//MissingMethodException:无签名。。属于m()。。适用于
//参数类型:(java.util.LinkedHashMap、java.lang.Integer、java.lang.String、java.lang.Integer)
//数值:[[a:aa,b:3],44,ss,5]
//7:未命名参数:失败!(将签名更改为添加默认值:Map args=[:],它将成功添加:s:ss n:44 x:1,args:[:]
m(“ss”,44)/…无签名…应用程序…类型(java.lang.String,java.lang.Integer)
//8:无命名参数:失败!(即使签名中有默认映射,也会失败!)
m(“ss”,44,5)/…无签名…应用程序…类型(java.lang.String、java.lang.Integer、java.lang.Integer)

我绝对讨厌groovy处理位置参数和命名/默认参数的方式。这太糟糕了。Python毫无疑问做到了这一点

问题
  • 使用参数名调用函数实际上会创建一个映射,并使该映射成为第一个参数
  • 代码

    这是不好的,因为它实际上改变了位置参数的顺序

  • 正常默认参数不能按顺序调用
  • 代码

    当然,您总是可以重写函数,使参数与您想要的位置匹配。当您有很多参数时,这只是一个巨大的麻烦

  • 使用映射作为第一个参数不允许使用纯位置参数
  • 代码

    我的解决方案。。。 最终,我的解决方案是将任意数量的参数作为对象,并将这些参数映射到已定义的参数映射

    代码

    //返回具有默认值的参数映射。如果参数为null,则返回错误
    def映射参数(对象参数,映射m){
    地图检查=[:]
    def偏移量=0
    //检查第一个参数是否为映射和设置值
    if(参数[0]映射实例){
    check=args[0]
    偏移量+=1
    检查。每个{子项->
    m[subitem.key]=subitem.value
    }
    }
    //Iter位置参数。不要替换映射值,因为它们是主参数。
    m、 每个Withindex{item,i->
    m[item.key]=((i+offset)def test(myParams, Integer i) {
        return myParams.a + myParams.b
    }
    
    assert test(a:'h',b:'i',5) == test(b:'i',a:'h',5)
    assert test([a:'h',b:'i'],5) == test(b:'i',a:'h',5)
    test('h','i',5); //still throws an exception
    
    test(a:"a", b: "b") // Actual myfunc([a: "a", b: "b"])
    test("a", b: "b")  // Actual myfunc([b: "b"], "a")
    test(a: "a", "b")  // Actual myfunc([a: "a"], "b")
    
    def test(String a, String b, int x=1, int y=2){
      a = args.get('a', a)
      b = args.get('b', b)
      x = args.get('x', x)
      y = args.get('y', y)
    
      println "a:$a b:$b x:$x, y:$y"
    }
    
    test("a", 'b')  // Positional arguments without giving the default values
    // "a:a b:b x:1 y:2"
    
    test("a", "b", 3)  // Positional arguments with giving 1 default and not the last
    // "a:a b:b x:3 y:2"
    
    test("a", "b", y:4)  // Positional with Keyword arguments. Actual call test([y:4], "a", "b")
    // This fails!? No signature of method, because Map is the first argument
    
    def test1(Map args=[:], String a, String b, int x=1, int y=2){
      a = args.get('a', a)
      b = args.get('b', b)
      x = args.get('x', x)
      y = args.get('y', y)
    
      println "test1(a:$a b:$b x:$x, y:$y, args:$args)"
    }
    
    test1("ss", "44", 5, c: "c", d: 3)  // Actual test2([c: "c", d: 3], "ss", "44", 5) Matches our definition
    // test1(a:ss b:44 x:5, y:2, args:[c:c, d:3, a:ss, b:44, x:5, y:2])
    
    test1(a: "aa", b: 3, "ss", "44", 5)  // Actual test2([a: "aa", b: 3], "ss", "44", 5) Nothing wrong with repeat parameters because they are in the map
    // test1(a:aa b:3 x:5, y:2, args:[a:aa, b:3, x:5, y:2])
    
    test1(a: "aa", b: 3, "ss", "44", y:5)  // Actual test2([a: "aa", b: 3, y:5], "ss", "44") y is in the map, so y still has the default positional value
    // test1(a:aa b:3 x:1, y:5, args:[a:aa, b:3, y:5, x:1])
    
    test1("ss", "44", y:3)  // Actual test2([y:3], "ss", "44")
    // test1(a:ss b:44 x:1, y:3, args:[y:3, a:ss, b:44, x:1])
    
    test1('a', 'b')  // Pure positional arguments only required arguments given (no defaults given)
    // test1(a:a b:b x:1, y:2, args:[a:a, b:b, x:1, y:2])
    
    test1("ss", "44", 5)  // Pure positional arguments one missing
    // This fails!? No signature of method. Why?
    
    test1("ss", "44", 5, 6)  // Pure positional arguments all arguments given
    // This fails!? No signature of method. Why?
    
    // Return a Map of arguments with default values. Error if argument is null
    def mapArgs(Object args, Map m){
      Map check = [:]
      def offset = 0
    
      // Check if first argument is map and set values
      if (args[0] instanceof Map){
        check = args[0]
        offset += 1
        check.each{ subitem ->
          m[subitem.key] = subitem.value
        }
      }
    
      // Iter positional arguments. Do not replace mapped values as they are primary.
      m.eachWithIndex{ item, i ->
        m[item.key] = ((i + offset) < args.size() && !check.containsKey(item.key)) ? args[i + offset] : item.value
        if (m[item.key] == null){
          throw new IllegalArgumentException("Required positional argument ${item.key}")
        }
      }
      return m
    }
    
    def test2(Object... args) {
      // println "args $args"
      def m = mapArgs(args, [a: null, b: null, x: 1, y:2])
      println "test2(a:$m.a b:$m.b x:$m.x, y:$m.y, args:null)"
    }
    
    test2("ss", "44", 5, c: "c", d: 3)  // Actual test2([c: "c", d: 3], "ss", "44", 5) Matches our definition
    // test1(a:ss b:44 x:5, y:2, args:[c:c, d:3, a:ss, b:44, x:5, y:2])
    // test2(a:ss b:44 x:5, y:2, args:null)
    
    test2(a: "aa", b: 3, "ss", "44", 5)  // Actual test2([a: "aa", b: 3], "ss", "44", 5) Nothing wrong with repeat parameters because they are in the map
    // test1(a:aa b:3 x:5, y:2, args:[a:aa, b:3, x:5, y:2])
    // test2(a:aa b:3 x:5, y:2, args:null)
    
    test2(a: "aa", b: 3, "ss", "44", y:5)  // Actual test2([a: "aa", b: 3, y:5], "ss", "44") y is in the map, so y still has the default positional value
    // test1(a:aa b:3 x:1, y:5, args:[a:aa, b:3, y:5, x:1])
    // test2(a:aa b:3 x:1, y:5, args:null)
    
    test2("ss", "44", y:3)  // Actual test2([y:3], "ss", "44")
    // test1(a:ss b:44 x:1, y:3, args:[y:3, a:ss, b:44, x:1])
    // test2(a:ss b:44 x:1, y:3, args:null)
    
    test2('a', 'b')  // Pure positional arguments only required arguments given (no defaults given)
    // test1(a:a b:b x:1, y:2, args:[a:a, b:b, x:1, y:2])
    // test2(a:a b:b x:1, y:2, args:null)
    
    test2("ss", "44", 5)  // Pure positional arguments one missing
    // This fails!? No signature of method. Why?
    // test2(a:ss b:44 x:5, y:2, args:null)
    
    test2("ss", "44", 5, 6)  // Pure positional arguments all arguments given
    // This fails!? No signature of method. Why?
    // test2(a:ss b:44 x:5, y:6, args:null)
    
    f(Map m=null, Object... obj)
    
    f([a:1], 2) has a map m[a:1] and one obj[0]=2
    
     f([a:1], b:2)