How can I parameterize access to a Java enum in clojure?
-
26-10-2019 - |
Pergunta
Say I have a Java enum. For example:
public enum Suits {CLUBS, DIAMONDS, HEARTS, SPADES};
Normally, I can do something in clojure with that enum like so:
(defn do-something []
(let [s Suits/DIAMONDS] (...)))
But, I want to write a clojure function that allows the caller to specify which enum instance to use:
(defn do-something-parameterized [suit]
(let [s Suits/suit] (...)))
The idea is to let a caller pass in "DIAMONDS"
and have the DIAMONDS
enum instance get bound to s
in the let
.
I could have a cond
match against the parameter but that seems clunkier than necessary. I suppose I could also use a macro to construct Suits/
added to suit
. Is this the way to do it or is there a non-macro way that I'm missing?
Solução
No need for reflection or maps. Every Java enum has a static valueOf
method that retrieves an enum value by name. So:
(defn do-something-parameterized [suit]
(let [s (Suit/valueOf (name suit))] ...))
Using (name)
allows either strings or keywords to be used:
(do-something-parameterized "HEARTS")
(do-something-parameterized :HEARTS)
Outras dicas
I asked a similar question a long while ago, not regarding enums but static class members in general: How can I dynamically look up a static class member in Clojure?
The answer was to use Java reflection:
(defn do-something-parameterized [suit]
(let [s (.get (.getField Suits suit) nil)] (...)))
To improve performance, you could create a map with the string that you want to match, to the enum type, for example:
(def my-enum-map {"DIAMONDS" Suits/DIAMONDS, "SPADES" Suits/SPADES...})
Then in the do something function it would look like:
(defn do-something-parameterized [suit]
(let [s (my-enum-map suit)] ...))
And you can build this map during load time using reflection (instead of by hand), but during runtime, its just a map lookup.
(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#)))