Java中闭包的模拟有意义吗?

Java中闭包的模拟有意义吗?,java,Java,具有闭包的语言(如Ruby)支持优雅的结构来转换列表。假设我们有一节课 class QueryTerm { String value; public String getValue() {...} } 还有一个术语列表list terms,我们希望将其转换为其值列表list values 在Ruby中,我们可以编写如下内容: values1 = terms.collect do |term| term.getValue() end Java迫使我们构建结果列表并自己迭代术语集

具有闭包的语言(如Ruby)支持优雅的结构来转换列表。假设我们有一节课

class QueryTerm {
  String value;
  public String getValue() {...}
}
还有一个术语列表
list terms
,我们希望将其转换为其值列表
list values

在Ruby中,我们可以编写如下内容:

values1 = terms.collect do |term|
    term.getValue()
end
Java迫使我们构建结果列表自己迭代术语集合(至少自从引入foreach以来,没有涉及迭代器或索引位置):

Collection values2=new HashSet();
for(查询项术语:术语){
values2.add(term.getValue());
}
并尝试使用匿名类在Java中模拟闭包:

Collection<String> values3 = Collections2.transform(terms, new Function<QueryTerm, String>() {
    @Override
    public String apply(QueryTerm term) {
        return term.getValue();
    }
});
Collection values3=Collections2.transform(术语,新函数(){
@凌驾
公共字符串应用(查询项){
返回项.getValue();
}
});
奇怪的是,这个版本比原始版本有更多的代码行和更多的字符!因为这个原因,我认为这更难理解。因此,当我在ApacheCommons中看到它时,我驳回了这个想法。然而,最近在谷歌收藏中看到它的出现,我开始怀疑自己是否遗漏了什么

因此,我的问题是:您对上述结构有何看法?你认为(<)>代码>版本比默认版本更可读/可理解吗?我完全错过了什么吗?

致以最良好的祝愿,
Johannes

IMO,即使有更多的代码行,在您的特定示例中,
transform
版本看起来更具可读性。为什么?因为这个词,转换。您立即知道集合中正在执行转换,而对于普通版本,您必须遵循代码流来理解它的意图。一旦你掌握了这些新结构,你会发现它们更具可读性。

你说的是什么。此外,一些IDE(例如Eclipse)可以隐藏匿名类,从而将Google集合代码缩减为一行。现在可以读了!:)

我喜欢并且经常使用它们。我相信主要的好处不是语法,而是您可以预定义和重用上面声明为匿名类的代码。例如,您可以预定义可以传递给transform函数的标准转换,甚至是不产生空对象模式效果的NullTransform。这允许您使用更具声明性的编码风格

这里使用的闭包与。我猜命令在这里有用的原因是什么。当然,
命令
也可能比闭包稍微成熟一些,因为后者仅在功能级别。但一个关键的观察是,这通常是你所需要的

对我来说,有两个优点是显而易见的:

  • 声明操作的函数式更容易映射到我们对操作的思考方式,即,这是我的函数,请将其应用于此数据
  • 转换的链接是通过透明地使用不同的调用来应用/过滤/转换的。您只需将它们彼此链接,就可以在一行中看到彼此之后收集的所有操作。当然,它仍然有很多样板文件,但如果做得好,任何java程序员都能很好地阅读它
  • 控制反转:假设您想在几个不同的线程上分解您的操作。如果您编写了自己的循环,那么您现在就陷入了困境:您必须自己将其分解为几个不同的循环。编写代码通常很难看,也很棘手,需要创建几个可运行程序,并将其传递给其他线程。所以,实际上你只是在重新使用闭包

    JDK7的其中一个建议显然是为您做了所有这些,您只需提交数据并定义要对其执行的过滤器,然后就可以随心所欲了。实际上,您可以为该软件包(166Y外包装)。您可以在该包的中看到的所有操作都可以消除,并用闭包代替。因此,这是一个很好的例子,说明了为什么某种方法由于缺少真正的闭包而失败,而“模拟”闭包不再能够完全解决这个问题

实际上,有很多问题可以通过传入一个简单的函数来解决,该函数可以对数据执行您希望它执行的操作,并让一个框架来填充其余的部分。您现在可以使用匿名类来实现这一点,但适当的闭包至少会减少很多锅炉板


我使用过的框架中有一个使用类似这种方法来删除大量样板文件的是。在这里,您将向JdbcTemplate传递一个简单的函数,该函数从
ResultSet
中获取对象,并将其与查询一起传递到
template.query()
。这就消除了通常JDBC代码的许多恼怒和不可读性。在软件开发中,明确易懂(阅读:可维护)比简洁更重要,除非您对一次性代码或perl脚本感兴趣:-)

如果您想要实现的是将对象列表转换为另一种类型的对象列表,那么您的第一个Java示例最清楚地说明了代码的意图,imho

GoogleCollecions的例子也清楚地说明了代码的意图,尽管正如Hannes所说,我不会使用匿名类,而是使用一个显式定义的、具有有用名称的类

Ruby中的语法糖使得在代码中引入转换变得很容易,当它们需要更改时,很容易错过转换。转换列表不是一个很好的方法
Collection<String> values3 = Collections2.transform(terms, new Function<QueryTerm, String>() {
    @Override
    public String apply(QueryTerm term) {
        return term.getValue();
    }
});
Collection<String> values2 = new HashSet<String>();
for (QueryTerm term : terms) {
    values2.add(term.getValue());
}
Collection<String> values2 = extract(terms, on(QueryTerm.class).getValue());