Wie kann ich den Zugriff auf eine Java -Enum in Clojure parametrisieren?
-
26-10-2019 - |
Frage
Sagen Sie, ich habe einen Java Enum. Zum Beispiel:
public enum Suits {CLUBS, DIAMONDS, HEARTS, SPADES};
Normalerweise kann ich etwas in Clojure mit diesem Enum so tun:
(defn do-something []
(let [s Suits/DIAMONDS] (...)))
Ich möchte jedoch eine Clojure -Funktion schreiben, mit der der Anrufer angeben kann, welche Enum -Instanz verwendet werden soll:
(defn do-something-parameterized [suit]
(let [s Suits/suit] (...)))
Die Idee ist, einen Anrufer einzugeben "DIAMONDS"
und haben die DIAMONDS
Enum -Instanz werden an gebunden an gebunden s
in dem let
.
Ich könnte eine haben cond
Übereinstimmung gegen den Parameter, aber das scheint klobiger als nötig. Ich nehme an, ich könnte auch ein Makro verwenden, um zu konstruieren Suits/
hinzugefügt zu suit
. Ist dies der Weg, dies zu tun, oder gibt es eine nicht-makrische Art und Weise, wie ich fehlt?
Lösung
Keine Reflexion oder Karten. Jeder Java Enum hat eine statische valueOf
Methode, die einen Enumswert nach Namen abruft. So:
(defn do-something-parameterized [suit]
(let [s (Suit/valueOf (name suit))] ...))
Verwendung (name)
Ermöglicht entweder Zeichenfolgen oder Schlüsselwörter:
(do-something-parameterized "HEARTS")
(do-something-parameterized :HEARTS)
Andere Tipps
Ich habe vor langer Zeit eine ähnliche Frage gestellt, nicht in Bezug auf Aufzüge, sondern statische Klassenmitglieder im Allgemeinen: Wie kann ich ein statisches Klassenmitglied in Clojure dynamisch nachsehen?
Die Antwort war, Java Reflection zu verwenden:
(defn do-something-parameterized [suit]
(let [s (.get (.getField Suits suit) nil)] (...)))
Um die Leistung zu verbessern, können Sie beispielsweise eine Karte mit der Zeichenfolge erstellen, die Sie übereinstimmen möchten, zum Beispiel:
(def my-enum-map {"DIAMONDS" Suits/DIAMONDS, "SPADES" Suits/SPADES...})
Dann würde es in der Funktion von etwas do the do etwas aussehen:
(defn do-something-parameterized [suit]
(let [s (my-enum-map suit)] ...))
Und Sie können diese Karte während der Ladezeit mit Reflexion (statt von Hand) erstellen, aber während der Laufzeit ist es nur eine Karten -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#)))