【问题标题】:Clojure, reflection: Find classes that implement an interfaceClojure,反射:查找实现接口的类
【发布时间】:2023-03-03 04:34:01
【问题描述】:

这在 Clojure 中似乎比在 Java 和 Scala 中更难。我想做的是:

  1. 在 Java 中定义接口
  2. 在 Clojure 的一个类中实现它(奖励:使用宏)
  3. 使用类加载器和反射找到它

这是我目前所拥有的:

Java 接口

package hello.interfaces;
public interface Test {
    String doSomething(String input);
}

Clojure 定义

(ns hello.impl.test
  (:gen-class
    :implements [hello.interfaces.Test]))

(defn doSomething [v] (str "hello " v))

搜索类:

(ns hello.core
  (:import
    (org.reflections Reflections)
    (org.reflections.util ConfigurationBuilder))
  (:gen-class))

(defn -instances []
  (let [paths (into-array Object ["hello"])]
      (.getSubTypesOf (Reflections. paths) hello.interfaces.Test)))

(defn -main [& args]
  (println (-instances)))

我正在使用org.reflections。如果我搜索类路径中的类(例如在org.reflections jar 中),则此方法有效,但它不适用于我之前定义的类,因此我认为问题不在代码的最后一个 sn-p 中,而是在前一个,或者可能在使用中,例如它需要预编译。

如何在 Clojure 中定义以后可以通过反射找到的类?

【问题讨论】:

  • 我必须说我对 Clojure 很陌生,这实际上是我正在尝试编写的第一段代码,很抱歉这个菜鸟问题。我对 Clojure 的主要兴趣是元编程,所以我希望使用宏实现接口的方式与这种用法兼容。在 Scala(以前的版本)中,我使用隐式转换而不是宏,但宏似乎更好地避免样板。
  • 从来没有以这种方式使用过 Java 接口,只要确保你知道 instance? - stackoverflow.com/a/12170685/1327651
  • @nha 我还不了解 Clojure 的大部分内容。在这种情况下,instance? 不是一个合适的选项,因为它与对象一起检查它们是否是类的实例。我正在寻找类,一旦找到,首先要做的就是调用它们的构造函数来构建它们对应的对象。我正在尝试做的可能不是传统的。

标签: java reflection interface clojure classpath


【解决方案1】:

我对 org.reflections 不熟悉,但如果你只是想要一个加载的类的列表,你可以用下面的代码得到它:

(let [classloader (.getClassLoader clojure.main)
      classes-field (.getDeclaredField java.lang.ClassLoader "classes")]
  (.setAccessible classes-field true)
  (let [class-list (.get classes-field classloader)
        class-vec (reduce conj [] class-list)] ; copy everything into a new vector rather than working directly with the classloader's private field
    class-vec))

听起来你对Java很熟悉,所以我猜你可以看到上面基本上只是翻译Java。它只会为您提供已使用与 clojure.main 类相同的类加载器加载的类,但如果您尚未对类加载器进行任何自定义,那就足够了。

获得该列表后,您可以根据需要搜索/过滤它。当然,您要查找的类必须首先加载。如果不是这种情况,则必须改为搜索类路径。

=== 更新以回复您的评论 ===

好的,我明白了,你在问如何创建一个类。首先要说的是,您在编写 Clojure 时通常不需要创建命名类,除非您特别想使用一些需要您这样做的现有 Java 代码。如果您正在编写纯 Clojure,您只需编写函数并直接使用它们。

但是,您当然可以这样做。 gen-class 文档的第一部分指出:

=>(文档生成类)

clojure.core/gen-class

([& 选项])

编译时,为类生成编译后的字节码 给定包限定的 :name (其中,作为这些中的所有名称 参数,可以是字符串或符号),并写入 .class 文件 到 compile-path 目录。不编译时,是否 没有。

所以,你需要编译你的命名空间。我通常不这样做,所以我不知道是否有一种方法可以在不创建 .class 文件的情况下直接在内存中创建类,但如果我理解正确的话,下面会做你想要的:

(ns wip.test)

; looks for wip/himplement.clj on the classpath, and compiles it into .class files
; requires that ../bin is in the classpath
(binding [*compile-path* "../bin"]
     (compile 'wip.himplement))

; loads the wip.himplement class from the .class files
(Class/forName "wip.himplement")


; create a list of all loaded classes (could presumably also be done with org.reflections)
(def classes-list (let [classloader (.getClassLoader clojure.main)
                        classes-field (.getDeclaredField java.lang.ClassLoader "classes")]
                    (.setAccessible classes-field true)
                    (java.util.ArrayList. (.get classes-field classloader))))

; Outputs all loaded classes which implement HInterface. Output is:
; (wip.hello.HInterface wip.himplement)
(println (filter #(isa? % wip.hello.HInterface) classes-list))

【讨论】:

  • 谢谢。org.reflections 为我提供了实现接口的类,但我无法在 Clojure 中创建实现接口的类。这是相应的代码:github.com/trylks/wip 你可以这样做lein run
  • 这行得通。谢谢你。问题是反射需要提前编译,有几种方法可以实现。一个是你提到的,另一个是 project.clj 给 Leiningen。我目前正在使用lein compile :all。也许值得一提的是,这对于依赖项来说不是问题,因为它们被编译并打包到.jar 文件中。
【解决方案2】:

我不完全确定您想要达到什么目标,但有几件事我可能应该为您指明方向。首先有一个名为clojure.reflect 的命名空间,其中包含用于获取类信息的有用函数。它使在 REPL 中反映 Java 层次结构变得更加容易。其次,clojure.tools.namespace 之类的东西将帮助您遍历 repo 中的代码并找到所有实现接口的命名空间。但是,您倾向于使用 deftype 来实现类,而不是使用 AOT 编译器功能,例如 ns 宏中的 :gen-class 选项,除非您要实现特定的东西,例如 serverlet 或其他东西。

【讨论】:

    猜你喜欢
    • 2010-12-03
    • 1970-01-01
    • 2021-09-26
    • 2010-09-09
    • 2014-04-05
    • 2011-09-18
    • 2015-10-21
    • 1970-01-01
    • 2011-10-31
    相关资源
    最近更新 更多