Как я могу параметризировать доступ к Java enum в Clojure?
-
26-10-2019 - |
Вопрос
Скажем, у меня есть Java enum. Например:
public enum Suits {CLUBS, DIAMONDS, HEARTS, SPADES};
Обычно я могу сделать что -то в Clojure с этим перечислением так:
(defn do-something []
(let [s Suits/DIAMONDS] (...)))
Но я хочу написать функцию Clojure, которая позволяет вызывающему абоненту указать, какой экземпляр Enum использовать:
(defn do-something-parameterized [suit]
(let [s Suits/suit] (...)))
Идея состоит в том, чтобы позволить вызывающему пройти "DIAMONDS"
и иметь DIAMONDS
Экземпляр Enum связан s
в let
.
Я мог бы иметь cond
Совместите с параметром, но это кажется неуклюжим, чем необходимо. Я полагаю, я мог бы также использовать макрос для построения Suits/
Добавлено в suit
. Анкет Это способ сделать это или есть не-макро, который мне не хватает?
Решение
Нет необходимости в отражении или картах. У каждого Java enum есть статический valueOf
Метод, который получает значение перечисления по имени. Так:
(defn do-something-parameterized [suit]
(let [s (Suit/valueOf (name suit))] ...))
С использованием (name)
Позволяет использовать строки или ключевые слова:
(do-something-parameterized "HEARTS")
(do-something-parameterized :HEARTS)
Другие советы
Я задавал аналогичный вопрос давно, не относился к перечислениям, а статичным членам класса в целом: Как я могу динамически искать статического члена класса в Clojure?
Ответ состоял в том, чтобы использовать Java Reflection:
(defn do-something-parameterized [suit]
(let [s (.get (.getField Suits suit) nil)] (...)))
Чтобы повысить производительность, вы можете создать карту со строкой, с которой вы хотите соответствовать, например:
(def my-enum-map {"DIAMONDS" Suits/DIAMONDS, "SPADES" Suits/SPADES...})
Тогда в функции Do Something это будет выглядеть:
(defn do-something-parameterized [suit]
(let [s (my-enum-map suit)] ...))
И вы можете построить эту карту во время загрузки, используя отражение (вместо ручной работы), но во время выполнения это просто поиск карты.
(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#)))