如何在clojure中参数化对Java枚举的访问?

时间:2011-09-05 21:52:00

标签: clojure clojure-java-interop

说我有一个Java枚举。例如:

public enum Suits {CLUBS, DIAMONDS, HEARTS, SPADES};

通常情况下,我可以用这样的枚举做一些clojure:

(defn do-something []
   (let [s Suits/DIAMONDS] (...)))

但是,我想编写一个clojure函数,允许调用者指定要使用的枚举实例:

(defn do-something-parameterized [suit]
   (let [s  Suits/suit] (...)))

我们的想法是让调用者通过"DIAMONDS"并让DIAMONDS枚举实例绑定到s中的let

我可以与参数进行cond匹配,但这似乎比必要的更笨拙。我想我也可以使用宏来构建添加到Suits/的{​​{1}}。这是做到这一点的方式还是有一种我失踪的非宏观方式?

4 个答案:

答案 0 :(得分:14)

无需反射或地图。每个Java枚举都有一个静态valueOf方法,可以按名称检索枚举值。所以:

(defn do-something-parameterized [suit]
  (let [s (Suit/valueOf (name suit))] ...))

使用(name)可以使用字符串或关键字:

(do-something-parameterized "HEARTS")
(do-something-parameterized :HEARTS)

答案 1 :(得分:1)

很久以前我问了一个类似的问题,不是关于枚举,而是关于静态类成员:How can I dynamically look up a static class member in Clojure?

答案是使用Java反射:

(defn do-something-parameterized [suit]
  (let [s (.get (.getField Suits suit) nil)] (...)))

答案 2 :(得分:0)

为了提高性能,您可以创建一个包含要匹配的字符串的映射到枚举类型,例如:

 (def my-enum-map {"DIAMONDS" Suits/DIAMONDS, "SPADES" Suits/SPADES...})

然后在do something函数中它看起来像:

 (defn do-something-parameterized [suit]
    (let [s (my-enum-map suit)] ...))

你可以在加载时使用反射(而不是手工)来构建这个地图,但在运行时,它只是一个地图查找。

答案 3 :(得分:0)

(defmacro def-enum-alias
  "Make name reference enum.

   (def-enum-alias enum-name MyClass$MyEnum)

   (enum-name Foo)

   second desugars to MyClass$MyEnum/Foo"
  [name enum]
  `(defmacro ~name
     ~(str "automatically generated alias for enum "
           enum)
     [member#]
     (symbol-munge (quote ~enum) "/" member#)))