追溯到刚开始学习 Groovy 还是在 2008 年,距今 2018 年有九年半余,曾记下几篇 Groovy 的日志。那时学习 Groovy 并无明确的目的,只因它是脚本语言, 可用来快速验证 Java API。曾经 BeanShell 芸花一现,JRuby 和 Jython 总是别人家的语言照搬而来的。而 Scala,Nashorn(jjs), JShell 更是后来的事,唯有 Groovy 写起来很亲切,完全不懂 Groovy 都没关系,直接上 Java。
现如今之所以重新勾搭上了 Groovy 是因为它仍然坚挺着,在 SoupUI(ReadyAPI) 和 Jenkins 中获得了重视,倒不是因为 Gradle。先前 Groovy 在 Spring 框架中的地位估计要被如今的 Kotlin 取而代之。
好了,回顾完后进入正题,关于 Groovy 如何进行多重赋值,以及延伸到方法返回多个值的情形。这里所说的多重赋值不是指用连等号来同时赋为一个值,
def a = b = c = 100 //不是说的这个
而是指同时对多个变量一步到位的赋成不同的值,要实现这个必须用到 List 类型。看下面一个基本例子(GroovyConsole)
基本语法就是
1 |
def(var1, var2, var3) = [value1, value2, value3] |
右边中括号声明的是一个 ArrayList
, 右边对等位置找到相应的值逐个赋给左边的变量, 前面的例子实际可以理解为下面的过程
1 2 3 |
list = [1, 'Yanbin] id = list.getAt(0) name = list.getAt(1) |
凡事都得举一反三
- 前面赋值是左右元素数目相等,如果是左短右长,或左长右短会是怎样
- 如果只需提取右端列表的某几个元素一条语句该如何做
- 函数如何返回多个值,以及如何一条语句接受函数返回的多个值
- 是否能一条语句获得自定义对象(非 List) 的多个属性值
- 正则表达式多组捕获的字符串是否能一次性的赋给多个变量
一般情况下我比较爱在 Groovy Shell 下进行简单的测试,但是发现 Groovy Shell 下用 def (a, b) = [1, 2]
的形式有个 Bug,去掉 def
关键字就可以了, 看下面的截图
加了 def
关键字后 id
和 name
两个变量反而不可见了,所以接下来的测试中我仍然用 Groovy Shell, 但是会把 def
关键字隐去。
左边变量数目多于右边列表元素
1 2 3 4 |
groovy:000> (id, name, address) = [1, 'Yanbin'] ===> [1, Yanbin] groovy:000> println "$id, $name, $address" 1, Yanbin, null |
左边多余的变量会被声明为 null
值
左边变量数目少于右边列表元素
1 2 3 4 |
groovy:000> (id, name) = [1, 'Yanbin', 'Earth'] ===> [1, Yanbin, Earth] groovy:000> println "$id, $name" 1, Yanbin |
列表从左自右只提取需要的元素分别赋值左边的变量
关于任意位置提取右边 List 的元素
Groovy 无法简单像 Scala 那样提取任意位置的元素,而必须显式的用 [index]
或 getAt(index)
方法。例如
1 2 3 4 |
groovy:000> list = [1, 2, 3, 4, 5] ===> [1, 2, 3, 4, 5] groovy:000> def (a, b, rest) = [0, 1, 2..-1].collect { list[it] } ===> [1, 2, [3, 4, 5]] |
上面直接用的 list[0], list[1], 和 list[2..01] 来得到 a, b, rest 的值。
而 Scala 从 Tuple 或各类集合中提取元素所展现的是它强大的模式匹配功能,Groovy 在这点上是不太具备的。
Groovy 方法同时返回多个值
有了以上的知识后,要实现 Groovy 方法返回多值就好办了,只要让方法返回一个 List 就行,接收端用 (a, b) = foo() 来进行多重赋值
1 2 3 4 |
groovy:000> def getUserInfo() { [1, 'Yanbin'] } ===> true groovy:000> def (id, name) = getUserInfo() ===> [1, Yanbin] |
从自定定义对象一次性提取多个属性值
比如,对于一个自定义对象 User,获得实例 user 后,需要分别用 user.id
和 user.name
来得到 id 和 name
1 2 3 4 5 6 7 8 |
@groovy.transform.Canonical class User { Integer id; String name; } def user = new User(1, 'Yanbin') println "${user.id}, ${user.name}" //输出 1, Yanbin |
那是否能用下面的形式一次性声明 id 和 name 两个变量,并从 user 实例中获得相应的值呢?
1 |
def (id, name) = new user(1, 'Yanbin') //有异常,见下 |
Groovy 报出异常
Exception thrown
groovy.lang.MissingMethodException: No signature of method: User.getAt() is applicable for argument types: (java.lang.Integer) values: [0]
Possible solutions: getAt(java.lang.String), getId(), setId(java.lang.Integer), getName(), putAt(java.lang.String, java.lang.Object), wait()
这个线索给予我们的是 User
类需要实现 getAt(Integer)
方法,因此对代码的改造如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@groovy.transform.Canonical class User { Integer id; String name; def getAt(Integer index) { switch(index) { case 0: id; break; case 1: name; break; default: throw new IndexOutOfBoundsException(String.valueOf(index)) } } } def (id, name) = new User(1, 'Yanbin') println "$id, $name" //输出为 1, Yanbin |
def (id, name) = new User(1, 'Yanbin')
相当于执行了
1 2 3 |
def user = new User(1, 'Yanbin') def id = user.getAt(0) def name = user.getAt(1) |
正则表达式中同时捕获多组值赋给多个变量
代码演示
1 2 |
groovy:000> def (exp, amount, currency) = ('12 Euro' =~ /(\d+) (\w+)/)[0] ===> [12 Euro, 12, Euro] |
exp
, amount
和 currency
的值分别为 12 Euro
, 12
和 Euro
。同样的,正则表达式分组中第一个元素是被匹配的字符串本身。
链接:
- Groovy Goodness: Multiple Assignments
- Getting tail of a list in Multiple Assignment in Groovy
- Groovy中的隐式构造函数
本文链接 https://yanbin.blog/groovy-multiple-assignment-returns/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
没有关注过groovy,它能解决什么问题的?
特定领域里用它,像 Jenkins, SoupUI(ReadyAPI), 就是写 Java 觉得有些罗嗦就换成 Groovy 来写。Groovy 并不是用来解决 Java 不容易解决的问题,而 Scala 比 Java 相对来说要高级些。看它们的生态,Groovy 有 Grails, Gradle,不足轻重,而 Scala 有 Spark, Kafka, PlayFramework, Akka,在业界的影响力就不可同日而语。
噢,这样的,受教。
我的理解中scala是属于函数式编程,java先jdk8开始也支持这样了,不太理解的是高级的地方是指什么方面?并没有了解scala,望见谅,可能问的有点低级。
可以说是 Scala 体现在函数式,以及语法上的精练,这就为什么那些高效框架要用 Scala 来写。再一方面,实际上 Scala 写的组件完全可以在 Java 中使用,但会出现某些奇怪的引用方式,所以像 Spark, Akka 那样纯 Scala 写的框架最好是用 Scala 来使用它。而 Kafka, PlayFramwork 核心是用 Scala 写的,但针对于 Java 特别实现有 Java 类库作为中介。
scala相较于java,是不是也像您上面说的那样,不同的体系。scala之于spack,kafka,而java之于spring,mybatis等等,是这样的意思吗?
好奇的一点是,高效框架,是指框架本身的性能比较高?还是使用scala开发的框架比较高效?还是使用scala开发程序比较高效呢?
Scala 语法上更趁手,更具表达力,所以那些框架采用 Scala 而不是 Java。基本不会有性能上的差异。
噢~,受教,明白了