是否可以将Clojure的案例表单与Java枚举一起使用?

时间:2013-05-27 17:18:51

标签: clojure clojure-java-interop

case doc说

  

与cond和condp不同,case执行恒定时间调度...所有方式都是常量   表达式是可以接受的。

我想从case的常量时间调度中获益,以便在Java枚举上进行匹配。 Java的switch语句适用于枚举,但在Clojure中执行以下操作:

(defn foo [x] 
   (case x 
      java.util.concurrent.TimeUnit/MILLISECONDS "yes!"))

(foo java.util.concurrent.TimeUnit/MILLISECONDS)

结果:IllegalArgumentException No matching clause: MILLISECONDS

case不支持枚举吗?难道我做错了什么?我必须诉诸cond还是有更好的解决方案?

3 个答案:

答案 0 :(得分:6)

这里的问题是case的测试常量,如文档中所述,“必须是编译时文字”。因此,正在测试文字符号java.util.concurrent.TimeUnit/MILLISECONDS,而不是解析'java.util.concurrent.TimeUnit/MILLISECONDS

(foo java.util.concurrent.TimeUnit/MILLISECONDS) ; IllegalArgumentException
(foo 'java.util.concurrent.TimeUnit/MILLISECONDS) ; yes!

相反,解决方案是在.ordinal实例的Enum上进行调度,这是Java在枚举时编译switch语句时本身所做的事情:

(defn foo [x]
  (case (.ordinal x)
    2 "yes!"))

您可以将此模式包装在宏中,以便为您正确评估案例序数:

(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)))))))

答案 1 :(得分:3)

您可以在枚举名称上使用避孕套

(case (.name myEnumValue) "NAME_MY_ENUM" (println "Hey, it works!"))

与替代方案相比,对我来说非常简单

答案 2 :(得分:0)

这是一个更简单的解决方案,只对案例使用等式检查 -

(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"