Groovy命名参数会导致参数分配发生切换——有没有办法解决这个问题?

Groovy命名参数会导致参数分配发生切换——有没有办法解决这个问题?,groovy,named-parameters,Groovy,Named Parameters,Groovy将所有命名参数收集到一个映射中,并将其作为第一个参数传递到一个方法中。这看起来很整洁,但在尝试使其工作后,它似乎真的无法使用 所以问题是这样一种方法: def method(paramMap, specificVar1 = 7, specificVar2 = 14) method(12, extraValue: "hello") method(12) 当您使用以下内容调用此方法时: def method(paramMap, specificVar1 = 7, specificV

Groovy将所有命名参数收集到一个映射中,并将其作为第一个参数传递到一个方法中。这看起来很整洁,但在尝试使其工作后,它似乎真的无法使用

所以问题是这样一种方法:

def method(paramMap, specificVar1 = 7, specificVar2 = 14)
method(12, extraValue: "hello")
method(12)
当您使用以下内容调用此方法时:

def method(paramMap, specificVar1 = 7, specificVar2 = 14)
method(12, extraValue: "hello")
method(12)
你得到的几乎是你所期望的:

assert 12 == specificVar1
assert 14 == specificVar2
assert [extraValue:"hello"] == paramMap
不错,有道理。问题是,如果您假设
映射
参数是可选的,那么您可以得到如下值:

def method(paramMap, specificVar1 = 7, specificVar2 = 14)
method(12, extraValue: "hello")
method(12)
这个标量应该进入specificVar,而不是映射。如果我在方法中专门键入映射:

def method(Map paramMap, specificVar1 = 7, specificVar2 = 14)

然后
方法(12,extraValue:“hello”)
的工作原理与以前一样,但是
方法(12)
抛出一个
ClassCastException
。这似乎不太有用。有没有办法让
Map
变得“粘性”,这样如果没有
Map
参数,它就会变成空的?

设置参数的默认值会创建从左到右组合的重载方法,因此,很难创建
方法(12)
,也很难传递映射条目

您的方法
def方法(paramMap,specificVar1=7,specificVar2=14)
将生成以下方法:

Object Maps.method(java.lang.Object)
Object Maps.method(java.lang.Object,java.lang.Object)
Object Maps.method(java.lang.Object,java.lang.Object,java.lang.Object)
Object Maps.method3()
Object Maps.method3(java.util.Map)
Object Maps.method3(java.util.Map,java.lang.Integer)
Object Maps.method3(java.util.Map,java.lang.Integer,java.lang.Integer)
以及具有map参数的全类型方法:

def method3(Map paramMap=[:], Integer specificVar1=7, Integer specificVar2=14) {
}
将生成以下方法:

Object Maps.method(java.lang.Object)
Object Maps.method(java.lang.Object,java.lang.Object)
Object Maps.method(java.lang.Object,java.lang.Object,java.lang.Object)
Object Maps.method3()
Object Maps.method3(java.util.Map)
Object Maps.method3(java.util.Map,java.lang.Integer)
Object Maps.method3(java.util.Map,java.lang.Integer,java.lang.Integer)
(对于
方法(12)
,没有合适的方法)

此外,传递给该方法的条目将被收集并插入到第一个map参数中。以下方法:

def method4(Integer specificVar1=7, Integer specificVar2=14, Map map=[:]) {
生成:

Object Maps.method4()
Object Maps.method4(java.lang.Integer)
Object Maps.method4(java.lang.Integer,java.lang.Integer)
Object Maps.method4(java.lang.Integer,java.lang.Integer,java.util.Map)
因此,
method4 12,a:'b'
失败的原因是:

No signature of method: Maps.method4() is applicable for argument types: 
  (java.util.LinkedHashMap, java.lang.Integer) values: [[a:b], 12]
所以,不,我不认为你可以用地图做你想做的事:-)


解决方案1:

如果要使用纯动态解决方案,可以使用单个映射参数:

def method5(Map map) {
  def specificVar1 = map.specificVar1 ?: 7
  def specificVar2 = map.specificVar2 ?: 14
}
解决方案2(更新):

您可以创建一个类来表示参数。使用要强制到对象中的映射是静态可编译的,并且为它提供了一个语法约束

@groovy.transform.CompileStatic
class Maps {
  def method6(Foo foo) { "$foo.params, $foo.specificVar1, $foo.specificVar2" }
  def method6(Map map) { method6 map as Foo }

  static main(args) {
    def maps = new Maps()

    assert maps.method6(params: [a: 'b', c: 'd'], specificVar1: 40) ==
        "[a:b, c:d], 40, 14"

    assert maps.method6(new Foo(params: [a: 'b', c: 'd'], specificVar2: 21)) == 
        "[a:b, c:d], 7, 21"
  }
}

class Foo {
  def specificVar1 = 7, specificVar2 = 14, params = [:]
}
解决方案3:

重载方法

def method6(Map paramMap, Integer specificVar1=7, Integer specificVar2=14) {
  "$paramMap, $specificVar1, $specificVar2"
}

def method6(Integer specificVar1=7, Integer specificVar2=14) {
  method6 [:], specificVar1, specificVar2
}


assert method6( 12 ) == "[:], 12, 14"
assert method6( ) == "[:], 7, 14"
assert method6( a:'b', 18 ) == "[a:b], 18, 14"
assert method6( 18, a:'b', 27 ) == "[a:b], 18, 27"
assert method6( 90, 100 ) == "[:], 90, 100"
assert method6( a:'b', 140, c:'d' ) == "[a:b, c:d], 140, 14"

map version方法不能有默认参数,否则两种方法都将生成无参数的
method6
,并且这些方法将冲突

很好的分析。我倾向于单一映射解决方案,但它使使用它变得更加困难(类型安全、易于查看的参数列表…),但它可能是我们在当前Groovy中所能做的最好的解决方案,这太棒了。我喜欢解决方案3!非常感谢你的时间和毅力。我打赌有一种方法可以从注释和methodMissing生成转发签名(这些类已经在扩展一个基类,所以我确信我可以用你的技巧让它完全按照我想要的方式工作)。我希望能有一种方法来奖励额外的销售代表——也许我可以用赏金,我需要调查一下!顺便说一下,我要做的是创建一个“类”,其中每个方法都可以从命令行运行,也可以从代码中轻松调用(一个类中有许多小“脚本”)。我目前使用(List,Map)并将“a=b”形式的CLI参数解析到Map中,然后将其余参数按顺序放入列表中——但使用此签名并不妨碍如何从代码中调用它,我始终必须考虑列表的参数/顺序。我不确定是否得到了它。。。您有一个类可以解析
groovy CliParser.groovy“coolClazz.method1(a,b,c,90.0)”
?目前我不使用CliParser,但我可以在我正在进行的下一次迭代中使用它——现在我手工解析它。每个“脚本”都扩展了父类——父类对命令行进行排序,查看第一个参数,在子类中找到一个以相同方式命名的方法,并使用其余参数调用该方法。每个类上的注释允许我打印任何类中可用的“脚本”(方法,以及注释中的文档文本)列表。它现在运行得很好,只是参数不好。Bill K,用一个非常简单的解决方案更新了我的答案。希望能有帮助。