Java 接口上的 Clojure Multimethod 调度
Clojure Multimethod dispatch on Java interface
假设我有一个 Java 方法 returns 某个父接口的对象。此函数返回的 类 对象未记录,但是有一个丰富且有据可查的接口层次结构,所有接口都扩展了父接口。例如:
public class Person {
public IMeal favoriteMeal() { ... }
}
public interface IBreakfast extends IMeal { ... }
public interface ILunch extends IMeal { ... }
public interface IBrunch extends IBreakfast, ILunch { ... }
如果我知道底层对象(并且对其稳定性有信心),我可以编写一个多方法来分派该方法返回的各种对象:
(defmulti place-setting class)
(defmethod place-setting Omelet [meal] ...)
但是,由于只有接口是 public,我宁愿派遣那些。是否有一种(好的)方式来调度接口?或许喜欢:
(defmulti place-setting magic-interface-dispatch-fn)
(defmethod place-setting IBreakfast [meal] ...)
您可以使用 multimethods 的调度函数将每个接口映射到一个 "dispatch value",然后使用这些调度值将事物定向到正确的代码。这个调度函数定义在接口之间的隐式层次结构中,所以你总是最终得到代码需要去的地方。
hello.core> (defmulti foo (fn [meal] (condp instance? meal
java.util.Collection ::breakfast
java.lang.Double ::lunch)))
nil
hello.core> (defmethod foo ::breakfast
[meal]
(count meal))
#multifn[foo 0x2b6e7712]
hello.core> (defmethod foo ::lunch
[meal]
meal)
#multifn[foo 0x2b6e7712]
hello.core> (foo 2.3)
2.3
hello.core> (foo [1 2 3])
3
如果在调度函数中定义层次结构很烦人,您可以切换到使用 clojure's built in hierarchy feature 以任意方式构建它们。
这已经很好用了:
注:
public interface IFn extends Callable, Runnable
public class Keyword implements IFn
然后:
(defmulti print-stuff class)
(defmethod print-stuff Callable [x] {:callable x})
(defmethod print-stuff :default [x] :not-found)
(print-stuff :foo) ;; => :callable
请注意,multimethods 始终在(可能是自定义的)层次结构内部使用 isa?
。 (isa? Keyword Callable)
是真的。
假设我有一个 Java 方法 returns 某个父接口的对象。此函数返回的 类 对象未记录,但是有一个丰富且有据可查的接口层次结构,所有接口都扩展了父接口。例如:
public class Person {
public IMeal favoriteMeal() { ... }
}
public interface IBreakfast extends IMeal { ... }
public interface ILunch extends IMeal { ... }
public interface IBrunch extends IBreakfast, ILunch { ... }
如果我知道底层对象(并且对其稳定性有信心),我可以编写一个多方法来分派该方法返回的各种对象:
(defmulti place-setting class)
(defmethod place-setting Omelet [meal] ...)
但是,由于只有接口是 public,我宁愿派遣那些。是否有一种(好的)方式来调度接口?或许喜欢:
(defmulti place-setting magic-interface-dispatch-fn)
(defmethod place-setting IBreakfast [meal] ...)
您可以使用 multimethods 的调度函数将每个接口映射到一个 "dispatch value",然后使用这些调度值将事物定向到正确的代码。这个调度函数定义在接口之间的隐式层次结构中,所以你总是最终得到代码需要去的地方。
hello.core> (defmulti foo (fn [meal] (condp instance? meal
java.util.Collection ::breakfast
java.lang.Double ::lunch)))
nil
hello.core> (defmethod foo ::breakfast
[meal]
(count meal))
#multifn[foo 0x2b6e7712]
hello.core> (defmethod foo ::lunch
[meal]
meal)
#multifn[foo 0x2b6e7712]
hello.core> (foo 2.3)
2.3
hello.core> (foo [1 2 3])
3
如果在调度函数中定义层次结构很烦人,您可以切换到使用 clojure's built in hierarchy feature 以任意方式构建它们。
这已经很好用了:
注:
public interface IFn extends Callable, Runnable
public class Keyword implements IFn
然后:
(defmulti print-stuff class)
(defmethod print-stuff Callable [x] {:callable x})
(defmethod print-stuff :default [x] :not-found)
(print-stuff :foo) ;; => :callable
请注意,multimethods 始终在(可能是自定义的)层次结构内部使用 isa?
。 (isa? Keyword Callable)
是真的。