【问题标题】:How do I pull `static final` constants from a Java class into a Clojure namespace?如何将 Java 类中的“静态 final”常量拉入 Clojure 命名空间?
【发布时间】:2010-04-03 18:57:29
【问题描述】:

我正在尝试使用 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-barfoo-some-constant

我认为这样做有两个原因:

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

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

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

谢谢

【问题讨论】:

    标签: clojure java-interop


    【解决方案1】:

    遗憾的是,宏 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)))
    

    【讨论】:

    【解决方案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的实现可以给你正确的想法,如果我上面的草图结果证明是不正确的。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • c.c.import-static, d'oh... 实际上在eval 的帮助下完全可以利用它。我冒昧地在编辑我的答案时使用它,这为intern + Java 反射技巧提供了另一种方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-09
      • 1970-01-01
      • 2013-09-18
      • 2012-07-22
      • 2012-08-10
      相关资源
      最近更新 更多