如何将`static final`常量从Java类拉入Clojure命名空间?

时间:2010-04-03 18:57:29

标签: clojure java-interop

我正在尝试使用Clojure绑定包装Java库。 Java库中的一个特定类定义了一堆静态最终常量,例如:

class Foo {
    public static final int BAR = 0;
    public static final int SOME_CONSTANT = 1;
    ...
}

我想到我可以检查这个类并将这些常量拉到我的Clojure命名空间中,而不是明确def - 每个。

例如,不要像这样明确地连接它:

(def foo-bar Foo/BAR)
(def foo-some-constant Foo/SOME_CONSTANT)

我可以检查Foo类,并在加载模块时在我的Clojure命名空间中动态连接foo-barfoo-some-constant

我认为有两个原因:

A)在将新常量添加到Foo类时自动引入它们。换句话说,在Java接口添加新常量的情况下,我不必修改我的Clojure包装器。

B)我可以保证常量遵循更多Clojure-esque命名约定

我并不是真的这么做,但是要求扩展我对Clojure / Java互操作的知识似乎是一个很好的问题。

由于

3 个答案:

答案 0 :(得分:3)

很遗憾,宏 clojure.contrib.import-static 不允许导入 所有 静态最终字段。您必须提供要导入的字段列表。

此宏是 import-static 的惯用包装:

(ns stackoverflow
  (:use clojure.contrib.import-static)
  (:import (java.lang.reflect Modifier)))

(defmacro import-static-fields
  "Imports all static final fields of the class as (private) symbols
  in the current namespace.

  Example: 
      user> (import-static-fields java.lang.Integer)
      #'user/TYPE
      user> MAX_VALUE
      2147483647

  Note: The class name must be fully qualified, even if it has already
  been imported."
  [class]
  (let [final-static-field? (fn [field]
                  (let [modifiers (.getModifiers field)]
                (and (Modifier/isStatic modifiers) (Modifier/isFinal modifiers))))
    static-fields (map #(.getName %)
               (filter
                final-static-field?
                (.. Class (forName (str class)) getFields)))]
    `(import-static ~class ~@static-fields)))

答案 1 :(得分:2)

(这个答案现在包括两个有效的解决方案,一个基于我最初的想法intern,一个基于danlei建议使用c.c.import-static。我想我需要清理这个稍后,但我现在不能花更多的时间......)

提取静态字段:

(filter #(bit-and java.lang.reflect.Modifier/STATIC (.getModifiers %))
        (.getFields YourClass))

然后在该序列中映射#(intern *ns* (str "a-prefix-" (.getName %)) (.get YourClass nil))以获取值...请注意此位未经测试,特别是,我不确定{{ 1}} nil;尝试使用.get并查看适合您班级的内容。

更新2:

好的,实际上基于java.lang.Field的方法并不是那么糟糕的可读性:

intern

更新: (保留第一个更新作为替代解决方案)

将danlei对user> (map #(intern *ns* (symbol (str "integer-" (.getName %))) (.get % java.lang.Integer)) (filter #(bit-and java.lang.reflect.Modifier/STATIC (.getModifiers %)) (.getFields java.lang.Integer))) (#'user/integer-MIN_VALUE #'user/integer-MAX_VALUE #'user/integer-TYPE #'user/integer-SIZE) user> integer-MIN_VALUE -2147483648 user> integer-MAX_VALUE 2147483647 user> integer-TYPE int user> integer-SIZE 32 的了解与上述结合起来得出以下结论:

clojure.contrib

它使用user> (map #(eval `(import-static java.lang.Integer ~(symbol (.getName %)))) (filter #(bit-and java.lang.reflect.Modifier/STATIC (.getModifiers %)) (.getFields java.lang.Integer))) (#'user/MIN_VALUE #'user/MAX_VALUE #'user/TYPE #'user/SIZE) user> MIN_VALUE -2147483648 user> MAX_VALUE 2147483647 user> TYPE int user> SIZE 32 ......好吧,那么,它几乎不会“杀死性能”并且它实际上相当可读,使用eval的详细表达可能不是。 (实际上并不是那么糟糕......: - )) 如果你更喜欢intern,至少intern的实现可以给你正确的想法如果上面的草图以某种方式证明是不正确的。

答案 2 :(得分:1)

我还没有尝试过,但也许clojure.contrib.import-static可以做到。

刚刚检查过:你必须在使用import-static时命名方法/字段,但我会在这里留下这个答案,因为它可能对搜索相关答案的人有所帮助。