如何将 Java 类中的“static final”常量拉入 Clojure 命名空间?

发布于 2024-08-27 18:45:06 字数 694 浏览 11 评论 0原文

我正在尝试使用 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 类并动态连接 foo-bar 和 foo-some加载模块时,我的 Clojure 命名空间中的 -constant 。

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

A) 在将新常量添加到 Foo 类中时自动引入新常量。换句话说,如果 Java 接口添加了新常量,我就不必修改 Clojure 包装器。

B) 我可以保证这些常量遵循更类似于 Clojure 风格的命名约定,

我并不真正愿意这样做,但这似乎是一个很好的问题,可以扩展我对 Clojure/Java 互操作的知识。

谢谢

I am trying to wrap a Java library with a Clojure binding. One particular class in the Java library defines a bunch of static final constants, for example:

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

I had a thought that I might be able to inspect the class and pull these constants into my Clojure namespace without explicitly def-ing each one.

For example, instead of explicitly wiring it up like this:

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

I'd be able to inspect the Foo class and dynamically wire up foo-bar and foo-some-constant in my Clojure namespace when the module is loaded.

I see two reasons for doing this:

A) Automatically pull in new constants as they are added to the Foo class. In other words, I wouldn't have to modify my Clojure wrapper in the case that the Java interface added a new constant.

B) I can guarantee the constants follow a more Clojure-esque naming convention

I'm not really sold on doing this, but it seems like a good question to ask to expand my knowledge of Clojure/Java interop.

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

遇到 2024-09-03 18:45:06

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

Sadly the macro clojure.contrib.import-static doesn't allow to import all static final fields. You must provide a list of fields to import.

This macro is a idiomatic wrapper for 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)))
黎歌 2024-09-03 18:45:06

(这个答案现在包括两个可行的解决方案,一个基于我对 intern 的最初想法,另一个基于 danlei 使用 ccimport-static 的建议。我想我'稍后需要清理它,但我现在不能花更多时间在上面...)

要提取静态字段:

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

Then map #(intern *ns* (str "a-prefix-" (.getName %)) (.get YourClass nil)) 穿过该序列来获取值...请注意,此位未经测试,特别是,我不确定 .get 中的 nil ;尝试一下 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 如果我上面的草图被证明是不正确的,可以给你正确的想法。

(This answer now includes two working solutions, one based on my initial idea with intern and one based on danlei's suggestion to use c.c.import-static. I guess I'll need to clean this up a bit later, but I can't spend more time on it now...)

To extract static fields:

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

Then map #(intern *ns* (str "a-prefix-" (.getName %)) (.get YourClass nil)) across that sequence to obtain the value... Note that this bit is untested and, in particular, I'm not sure about that nil in .get; experiment with java.lang.Field and see what works with your class.

Update 2:

Ok, actually an intern based approach is not that bad readability-wise:

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

Update: (leaving the first update in place as an alternative solution)

Combining danlei's knowledge of clojure.contrib with the above yields the following:

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

It uses eval... well, so what, it's hardly going to "kill performance" and it's actually fairly readable, which an elaborate expression using intern might not be. (It's not so bad actually... :-)) If you prefer intern, though, at least the implementation of import-static can give you the proper ideas if my sketch above turns out to be incorrect somehow.

趁年轻赶紧闹 2024-09-03 18:45:06

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

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

I haven't tried it, but maybe clojure.contrib.import-static can do it.

Just checked: You will have to name the methods/fields when using import-static, but I'll leave this answer here because it might be helpful for people searching for related answers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文