7

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

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

我有一个想法,我可能能够检查该类并将这些常量拉入我的 Clojure 命名空间,而无需显式地对def每个常量进行 -ing。

例如,不要像这样显式地连接它:

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

加载模块时,我将能够检查Foo该类并在我的 Clojure 命名空间中动态foo-bar连接foo-some-constant

我看到这样做的两个原因:

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

B)我可以保证常量遵循更 Clojure 风格的命名约定

我并没有真正喜欢这样做,但问这个问题似乎是一个很好的问题,可以扩展我对 Clojure/Java 互操作的了解。

谢谢

4

3 回答 3

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)))
于 2010-04-04T12:14:51.103 回答
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))该序列以获得值...请注意,该位未经测试,特别是,我不确定nil; .get试验一下java.lang.Field,看看什么对你的班级有效。

更新 2:

好的,实际上intern基于方法的可读性并没有那么差:

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

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

将 danlei 的知识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至少实施可以为您提供正确的想法。import-static

于 2010-04-03T19:29:51.940 回答
1

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

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

于 2010-04-03T19:30:49.503 回答