问题
The case
doc says
Unlike cond and condp, case does a constant-time dispatch... All manner of constant expressions are acceptable in case.
I would like to benefit from case
's constant-time dispatch to match on Java enums. Java's switch
statement works well with enums, but doing the following in Clojure:
(defn foo [x]
(case x
java.util.concurrent.TimeUnit/MILLISECONDS "yes!"))
(foo java.util.concurrent.TimeUnit/MILLISECONDS)
Results in: IllegalArgumentException No matching clause: MILLISECONDS
Are enums not supported in case
? Am I doing something wrong? Must I resort to cond
or is there a better solution?
回答1:
The problem here is that case
's test constants, as described in the docs, " must be compile-time literals". So, rather than resolving java.util.concurrent.TimeUnit/MILLISECONDS
, the literal symbol 'java.util.concurrent.TimeUnit/MILLISECONDS
is being tested against.
(foo java.util.concurrent.TimeUnit/MILLISECONDS) ; IllegalArgumentException
(foo 'java.util.concurrent.TimeUnit/MILLISECONDS) ; yes!
Instead, the solution is to dispatch on the .ordinal
of the Enum
instance, which is what Java itself does when compiling switch
statements over enums:
(defn foo [x]
(case (.ordinal x)
2 "yes!"))
You can wrap this pattern in a macro which correctly evaluates the case ordinals for you:
(defmacro case-enum
"Like `case`, but explicitly dispatch on Java enum ordinals."
[e & clauses]
(letfn [(enum-ordinal [e] `(let [^Enum e# ~e] (.ordinal e#)))]
`(case ~(enum-ordinal e)
~@(concat
(mapcat (fn [[test result]]
[(eval (enum-ordinal test)) result])
(partition 2 clauses))
(when (odd? (count clauses))
(list (last clauses)))))))
回答2:
You could use use a cond on the name of the enumm
(case (.name myEnumValue)
"NAME_MY_ENUM" (println "Hey, it works!"))
Seems to me very simple compared to the alternatives
回答3:
Here's a simpler solution that just uses equality checking on the cases -
(defn cases [v & args]
(let [clauses (partition 2 2 args)]
(some #(when (= (first %) v) (second %)) clauses)))
=> (cases EventType/received EventType/send "A" EventType/received "B")
=> "B"
来源:https://stackoverflow.com/questions/16777814/is-it-possible-to-use-clojures-case-form-with-a-java-enum