【问题标题】:How do I create a Java-like object in Clojure that uses builder pattern?如何在 Clojure 中创建一个使用构建器模式的类 Java 对象?
【发布时间】:2012-02-07 22:55:38
【问题描述】:

使用 Clojure,我如何创建以下对象?该对象取自java代码(From Effective Java):

NutritionFacts cocaCola = new NutritionFacts.Builder(240,8).calories(100).sodium(35).carbohydrate(27).build();

【问题讨论】:

    标签: clojure


    【解决方案1】:

    虽然很难与其他答案中的简洁性争论1,但.. 已经有些失宠,取而代之的是更通用的->。我个人更喜欢:

    (-> (NutritionFacts$Builder. 240 8) 
        (.calories 100)
        (.sodium 350)  
        (.carbohydrates 27) 
        (.build))
    

    多了几个字符,但你得到了两件事:

    • 明确性。我可以查看钠线(例如)并判断它是 Java 方法调用,因为 . 就在那里。
    • 灵活性。如果需要,我可以在中间链接一些非方法调用(例如,将其打印到标准输出),或者在所有这些结束时将其提供给其他函数调用。

    最重要的是,这个问题的所有其他答案都把类名弄错了:Java 的 NutritionFacts.Builder 是名为 NutritionFacts$Builder 的真正 JVM 类的语言糖,而该类是 Clojure 必须引用的类(因为我们不是使用 javac 编译我们的代码)。

    1 我不同意doto 的建议:它之所以有效,只是因为这个 Builder 类碰巧通过改变单个实例然后返回它来实现其方法链。 doto 非常适合需要就地突变的 Java 对象,但是当一个类足以假装它是不可变的时,您真的应该使用方法链接版本(即,->)。

    【讨论】:

    • 我需要导入builder类,这是真正的JVM类名(如文中所述)(:import [com.blah.blah NutritionFacts$Builder])
    【解决方案2】:

    使用.. 宏。这是两个连续的点。它可以满足您的需要 - 在前一个的结果上连续调用下一个 Java 方法。

    我没有 REPL,但你的行应该翻译成这样的:

    (.. (NutritionFacts.Builder. 240 8) 
        (calories 100)
        (sodium 350)  
        (carbohydrates 27) 
        (build))
    

    【讨论】:

      【解决方案3】:

      我只是从 Clojure 开始,但对我来说它看起来像是标准方法调用:

      (doto
        (NutritionFacts.Builder. 240 8)
        (.carbohydrates 27)
        (.sodium 35)
        (.calories 100)
        (.build)
      )
      

      编辑:
      正如@Goran Jovic 指出的那样,这会调用以第一种形式创建的对象的所有方法。
      所以它在这种情况下有效,因为 Java 代码使用方法链,但不是更普遍适用。

      【讨论】:

      • 它不是惯用的唯一原因是因为您以几种不同的形式对 var 进行了变异。只需将其包裹在 doto 中即可!
      • @Goran Jovic 我已按照您的建议更改了代码。谢谢!
      • 是的,就是这样。请注意doto.. 之间的主要区别在于doto 调用第一种形式定义的对象上的方法,而.. 对前一个形式的结果进行每次调用。在这种情况下,它是相同的,因为 Java 方法不断返回正在构建的对象。
      • @GoranJovic:值得添加到答案中;用户应该意识到这一点。再次感谢!
      【解决方案4】:

      (.. (NutrionalFacts.Builder. 240 8) (calories 100) (sodium 35) (carbohydrates 27) (build))

      【讨论】:

        【解决方案5】:

        这个线程已有几年历史了,据我所知,线程优先宏仍然是首选方式,但不需要 $ 语法。 斜线 (/) 也可以:

        (-> (Caffeine/newBuilder)
              (.maximumSize 10000)
              (.build))
        

        【讨论】:

        • (Caffeine/newBuilder) 是对静态方法的调用 (Java Caffeine.newBuilder()) 而(NutritionalFacts$Builder.) 是对静态类的构造函数的调用 (Java new NutritionalFacts.Builder()),所以这真的不是t 与接受的答案有任何不同。
        • 啊,好的。我认为语法有一些更新。谢谢
        猜你喜欢
        • 1970-01-01
        • 2014-02-15
        • 1970-01-01
        • 1970-01-01
        • 2020-03-28
        • 2021-08-11
        • 1970-01-01
        • 1970-01-01
        • 2012-08-04
        相关资源
        最近更新 更多