Clojure defprotocol作为表达问题的解决方案

Ral*_*lph 9 polymorphism clojure

在"Clojure的喜悦"一书中,defprotocol提供了表达式问题的解决方案- "希望为现有的具体类实现一组现有的抽象方法,而不必更改定义任何一个的代码."

给出的例子如下:

(defprotocol Concatenatable
  (cat [this other]))

(extend-type String
  Concatenatable
  (cat [this other]
    (.concat this other)))

(cat "House" " of Leaves")
;=> "House of Leaves"

(extend-type java.util.List
  Concatenatable
  (cat [this other]
    (concat this other)))

(cat [1 2 3] [4 5 6])
;=> (1 2 3 4 5 6)
Run Code Online (Sandbox Code Playgroud)

有人认为这在Java这样的语言中是不可能的,但它与以下内容有何不同?

public class Util {
  public static String cat(final String first,
                           final String second) {
    return first.concat(second);
  }

  public static <T> List<T> cat(final List<T> first,
                                final List<T> second) {
    final List<T> list = new List<T>(first);
    list.addAll(second);
    return list;
  }
}
Run Code Online (Sandbox Code Playgroud)

毕竟,两者都是类似的:

(cat "House" " of Leaves")
Util.cat("House", " of Leaves");
Run Code Online (Sandbox Code Playgroud)

Clojure的功能cat一个方法StringList类,而是一个独立的功能被重载以接受任一StringList参数.

虽然我真的很喜欢Clojure,但我不理解这种结构的优越性.

ama*_*loy 21

好的.你cat大张旗鼓地释放这个Java库,每个人都下载它.它太棒了我想让我自己的TVCommercial类型可以连接,这样我就可以将它发送到你的库中可连接对象的位.

但我不能,因为你打电话Util.cat(obj1, obj2),没有超载TVCommercial.我无法扩展您的代码来处理我的类型,因为我没有您的代码.

您可以将Concatenable定义为解决此问题的接口:

interface Concatenable {
  Concatenable cat(Concatenable other);
}
Run Code Online (Sandbox Code Playgroud)

但是现在我不能写一个可以连接的类......我不知道,一个AnimalHandler,它处理cats.Clojure的协议通过分散调度功能和实现来解决这两个问题:它们遍布整个地方,而不是在某个单独的位置.在Java中,您可以选择:

  • 将所有类型调度放入单个开关/案例或重载方法
  • 定义一个接口,强制使用特定名称的方法

Clojure基本上完成了后者,但由于它使用了命名空间名称,因此没有与其他认为cat是一个好函数名称的协议冲突的危险.