我非常讨厌 groovy 如何处理位置和命名/默认参数。它是可怕的。 Python 毫无疑问地做到了。
问题
- 使用参数名称调用函数实际上会创建一个映射并将该映射作为第一个参数。
代码
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
当然,您始终可以重写该函数以使参数与您想要的位置相匹配。当你有很多争论时,这只是一个巨大的麻烦。
- 使用 Map 作为第一个参数不允许使用纯位置参数
代码
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)
我对这个解决方案不太满意,但它使关键字参数可以满足我的需求。