Можно ли использовать форму case Clojure с перечислением Java?

В документе case говорится

В отличие от cond и condp, случай отправляет постоянное время... Весь способ постоянной выражения допустимы в случае.

Мне хотелось бы получить от 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 или есть лучшее решение?

Ответ 1

Проблема заключается в том, что тестовые константы 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)
       [email protected](concat
          (mapcat (fn [[test result]]
                    [(eval (enum-ordinal test)) result])
                  (partition 2 clauses))
          (when (odd? (count clauses))
            (list (last clauses)))))))

Ответ 2

Вы можете использовать использование cond для имени enumm

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

Мне кажется очень простым по сравнению с альтернативами

Ответ 3

Здесь более простое решение, которое просто использует проверку равенства на случаях -

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