# Groovy 使用 GPath 来查询对象

**URL:** <https://www.isharkfly.com/t/groovy-gpath/1943>\
**Category:** 程序语言\
**Created:** [December 15, 2012, 2:01am UTC](https://www.isharkfly.com/t/groovy-gpath/1943 "2012-12-15T02:01:18Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![honeymoose](https://www.isharkfly.com/user_avatar/www.isharkfly.com/honeymoose/32/20796_2.png) [@honeymoose](https://www.isharkfly.com/u/honeymoose)\
**Post date:** [December 15, 2012, 2:01am UTC](https://www.isharkfly.com/t/groovy-gpath/1943/1 "2012-12-15T02:01:18Z")

</div>

我们通过反射 API 探讨 groovy，目标是获取一个当前对象的所有 getter 方法的简短列表，我们将一步一步的做，因此请打开 groovyConsole 并且跟随向前，你将试图获取到当前对象的信息，输入

this

并且运行脚本（输入 Ctrl-Enter），在输出窗口，你将看到像这样的一些信息

Script1@e7e8eb

这是当前对象的字符串表示结果，为了获取到当前对象的 class 的相关信息，你能够使 用 this.getClass，但是在 groovy 中你能输入

this.class

显示的结果为（在你运行该脚本之后）

class Script2

通过 getMethods 来获取到 class 对象暴露的方法列表，因此输入：

this.class.methods

结果输出了一个大的方法对象描述列表，这里有太多的信息，你仅仅对方法的名称感兴趣，每一个方法对象有 getName 方法，因此这样调用

this.class.methods.name

并且得到一个方法名称的列表，作为一个字符串对象的列表返回，你可以容易的使用学 到的字符串，正则表达式，列表等相关知识进行工作。由于你仅仅对 getter 方法感兴趣，并且想对这些方法排序，输入

this.class.methods.name.grep(~/get.\*/).sort()

你将得到这样的结果

[“getBinding”, “getClass”, “getMetaClass”, “getProperty”]

例如这样的表达式叫做 GPath，一个特殊的事情是你能够在一个方法对象的列表上调用 name 属性并且接收到一个字符串对象的列表。

后台规则是：

list.property

等价于

list.collect{ item → item?.property }

这是在列表中进行属性访问的特殊用法的缩写，一般用法为：

list\*.member

这里\*.叫做展开点操作符，并且 member 能够作为属性被访问，一个属性访问，或者 一个方法调用，展开点操作符在方法应用到列表中的所有元素上的时候是需要的，而不是应用到列表本身上，它的等价物为：

list.collect{ item → item?.member }

为了在实战中明白 GPath，我们看一个适当接近真实情况的例子，假如你正在处理一组发票，每一组引用到固定产品，一个产品有一个价格和名称。

一张发票看起来像表 7.3

![](https://www.isharkfly.com/images/transparent.png)

图 7.1 使用 UML 类图描述了相应的软件模型，Invo ice 类包含多个指向产品的 LineItem。

列表 7.23 是设计的 groovy 实现，定义类为 groovyBean，通过这个结构构建简单的发票，并且最后使用 GPath 表达式通过多种途径查询对象图。

![](https://www.isharkfly.com/images/transparent.png)

 ![](https://com-ossez-www-discourse.s3.dualstack.us-east-2.amazonaws.com/discourse-uploads/original/2X/a/a952c94ed960c177708093aec803b4bfbd526aac.png)

在列表 7.23 的问题是复杂难懂，首先，在（1），找到每一张发票的总价，这是将所有的项目累加起来。然后我们在（2）查询项目总费用超过 7000 元的所有产品的名称，最后，在（3）查找包含购买了 ULC 产品的每一张发票的日期，并且将它们转换为字符串。

打印出完全的 java 等价实现代码将需要超过 4 页内容并且阅读是非常无趣的，如果你想阅读这个代码，你能在本书的在线资源中找到。

Gpath 和相应的 java 代码的比较是有趣的，Gpath：`invoices.items.grep{ it.total() > 7000 }.product.name`相应的 java 代码为：[code]// Java

private static List getProductNamesWithItemTotal(Invoice

invoices) {

List result = new LinkedList();

for (int i = 0; i \< invoices.length; i++) { List items = invoices[i].getItems();  
for (Iterator iter = items.iterator(); iter.hasNext()😉 { LineItem lineItem = (LineItem) iter.next();  
if (lineItem.total() \> 7000){

result.add(lineItem.getProduct().getName());

}

}

}

return result;

}  
[/code]表 7.4 给出了两个版本的一些材料，比较代码的行数（LOC），语句数，及嵌套的复杂程度。

也许可以对 java 版本的代码进行裁剪，但是还是有数量级的差别：groovy 需要的代码量至少比 java 少 25%，并且语句少 10%！

写更少的代码不仅仅是一个实践的理由，这也意味着犯更少的错误和更少的测试工作。

而有些新开发人员考虑的是一天写多少行代码，而我们考虑的是一天完成的功能。

在许多语言中，较少的代码导致意思的不明确，但在 groovy 中不是这样，Gpath 的例子是最好的证明，它比 java 对应物更容易阅读和理解，即使是复杂的逻辑。

![](https://www.isharkfly.com/images/transparent.png)

最后，考虑一下可维护性，假设你的客户完善他们的需求，并且你需要改变查找逻辑。
