代码之家  ›  专栏  ›  技术社区  ›  Joe23

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

  •  8
  • Joe23  · 技术社区  · 16 年前

    具有闭包的语言(如Ruby)允许优雅的构造转换列表。假设我们有一个班

    class QueryTerm {
      String value;
      public String getValue() {...}
    }
    

    以及一份条款清单 List<QueryTerm> terms ,我们要转换成它的值列表 List<String> values .

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

    values1 = terms.collect do |term|
        term.getValue()
    end
    

    爪哇迫使我们 构建结果列表 循环访问术语集合 我们自己(至少在引入foreach之后不涉及迭代器或索引位置):

    Collection<String> values2 = new HashSet<String>();
    for (QueryTerm term : terms) {
        values2.add(term.getValue());
    }
    

    Apache Commons Google Collections2 尝试使用匿名类模拟Java中的闭包:

    Collection<String> values3 = Collections2.transform(terms, new Function<QueryTerm, String>() {
        @Override
        public String apply(QueryTerm term) {
            return term.getValue();
        }
    });
    

    奇怪的是,这个版本比原始版本有更多的代码行和字符!因为这个原因,我认为很难理解。因此,当我在ApacheCommons看到这个想法时,我就把它驳回了。然而,最近在谷歌收藏中看到了它,我开始怀疑我是否遗漏了一些东西。

    因此,我的问题是:你对上述结构有什么看法?你认为 Collections2.transform() 版本比默认版本更可读/易懂?我完全错过什么了吗?

    最好的问候, 约翰尼斯

    7 回复  |  直到 16 年前
        1
  •  4
  •   bruno conde    16 年前

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

        2
  •  5
  •   Jim Ferrans    16 年前

    这里使用的闭包与 Command pattern .我猜命令在这里可能有用的任何原因。A Command 当然,也可以比闭包稍微成熟一点,因为后者只在功能级别上。但关键的观察是,这通常是你所需要的。

    对我来说,有几个优势是显而易见的:

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

      JDK7的其中一个提议显然不能让它成为现实,这一切都是为您做的,您只需提交您的数据并定义要对其执行的过滤器,然后您就让它快乐地前进了。实际上你可以 download the code 为此(第166Y包)。你能看到的所有行动 javadoc 因为这个包装可以消除和替换为闭包。因此,这是一个很好的例子,为什么某个方法由于缺少真正的闭包而失败,并且“模拟”闭包不再是那么简单了。

    实际上,通过传递一个简单的函数来完成您希望它对数据所做的工作,并让一个框架填充其余的工作,您可以真正轻松地解决大量的问题。您现在可以使用匿名类来实现这一点,但是适当的闭包至少会减少很多锅炉盘。

    我使用过的一个框架使用类似于这种方法来减少很多样板文件 Spring-Jdbc . 在这里,您将传递一个简单的函数jdbctemplate,该函数从 ResultSet 然后将它与查询一起传递给 template.query() .这就减少了许多加重和不可读的常见JDBC代码。如果你能保持这样的状态,一定会赢。

        3
  •  2
  •   Hannes de Jager Ruben    16 年前

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

        4
  •  1
  •   Community Mohan Dere    9 年前

    什么 bruno conde 说。此外,一些IDE(例如Eclipse)可以隐藏匿名类,将Google集合代码缩小到一行。现在 那个 是可读的!:)

        5
  •  1
  •   rsp    16 年前

    在软件开发中,显式和可理解(读:可维护)比简洁更重要,除非您喜欢使用一次性代码或Perl脚本:—)

    如果您想要实现的是将对象列表转换为另一类型的对象列表,则第一个Java示例是关于代码意图的最清晰的,IMHO。

    Google Collecions的例子也清楚地说明了代码的意图,尽管正如汉内斯所说,我不会使用匿名类,而是使用一个有有用名称的明确定义的类。

    Ruby中的语法糖分使在代码中引入转换变得容易,当需要更改时,很容易错过一个转换。转换列表也不是一个很便宜的功能,并且能够很容易地使用它们会带来过度使用的风险。

        6
  •  0
  •   irreputable    16 年前

    它非常丑陋,人们渴望更好的语法。一段时间后你就可以习惯了,但对初学者来说,这是一种威胁。

    忘记@override-代码已经混乱。@override解决了现实中不存在的问题,但人们现在却把它当作一个关键的语义元素来使用。

        7
  •  0
  •   Mario Fusco    16 年前

    函数式编程不是你可以通过使用它来保存的许多击键的问题,主要是它如何提高代码的可读性。例如,通过使用 lambdaj 您可以将代码段转换为:

    Collection<String> values2 = new HashSet<String>();
    for (QueryTerm term : terms) {
        values2.add(term.getValue());
    }
    

    在这一个:

    Collection<String> values2 = extract(terms, on(QueryTerm.class).getValue());
    

    你不觉得这样读起来更容易吗?