- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我一直在阅读代码和文档,试图了解类重新加载在 clojure 中的工作原理。根据很多网站,比如http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html ,每当您加载一个类时,本质上您都会获取字节码(通过任何数据机制),将字节码转换为类 Class 的实例(通过defineClass),然后通过resolveClass解析(链接)该类。 (defineClass是否隐式调用resolveClass?)。任何给定的类加载器只允许链接一个类一次。如果它尝试链接现有的类,则不会执行任何操作。这会产生一个问题,因为您无法链接新实例化的类,因此每次重新加载类时都必须创建类加载器的新实例。
回到 clojure,我尝试检查加载类的路径。
在 clojure 中,您可以根据需要以多种方式定义新类:
匿名类:具体化代理
命名类:定义类型defrecord(在底层使用 deftype)世代级
最终,这些代码指向 clojure/src/jvm/clojure/lang/DynamicClassLoader.java
其中DynamicClassLoader/defineClass使用super的defineClass创建一个实例,然后缓存该实例。当您想要检索类时,clojure 通过调用 forName 来加载,它调用类加载器和 DynamicClassLoader/findClass,它在委托(delegate)给父类(super class)之前首先在缓存中查找(这与大多数普通类加载器的工作方式相反,它们在首先委托(delegate),而不是自己尝试。) 重要的混淆点如下:forName 被记录为在返回之前链接该类,但这意味着您无法从现有的 DynamicClassLoader 重新加载类,而是需要创建一个新的 DynamicClassLoader,但是我在代码中没有看到这一点。 我知道 proxy 和 reify 定义了匿名类,因此它们的名称不同,因此可以将其视为不同的类。然而,对于指定的类,这种情况就失效了。在真正的 clojure 代码中,您可以同时引用旧版本的类和新版本的类,但尝试创建新的类实例将是新版本的。
请解释一下 clojure 如何能够在不创建 DynamicClassLoader 新实例的情况下重新加载类,如果我能理解重新加载类的机制,我想将此重新加载功能扩展到我可以使用 javac 创建的 java .class 文件。
注释:这个问题指的是类RELOADING,而不是简单的动态加载。重新加载意味着我已经实习了一个类,但想要实习该实例的新更新版本。
我想重申,目前尚不清楚 clojure 如何重新加载 deftype 定义的类。调用 deftype 最终会导致调用 clojure.lang.DynamicClassLoader/defineClass。再次执行此操作会导致再次调用 DefineClass,但手动执行此操作会导致链接错误。这里到底发生了什么,允许 clojure 使用 deftypes 执行此操作?
最佳答案
并非所有这些语言功能都使用相同的技术。
proxy
宏仅根据继承的类和接口(interface)列表生成类名。此类中每个方法的实现委托(delegate)给存储在对象实例中的 Clojure fn。这允许 Clojure 在每次继承相同的接口(interface)列表时使用完全相同的代理类,无论宏体是否相同。没有实际的类重新加载发生。
对于reify
,方法体直接编译到类中,因此proxy
使用的技巧不起作用。相反,编译表单时会生成一个新类,因此如果更改表单主体并重新加载它,您将获得一个全新的类(具有新生成的名称)。同样,没有发生实际的类重新加载。
使用gen-class
,您可以为生成的类指定一个名称,因此用于proxy
或reify
的技术都不起作用。 gen-class 宏仅包含类的某种规范,但不包含任何方法体。生成的类有点像 proxy
,遵循方法体的 Clojure 函数。但由于名称与规范相关联,与 proxy
不同,它无法更改 gen-class
的主体并重新加载它,因此 gen-class
仅在提前编译(AOT 编译)时可用,并且在不重新启动 JVM 的情况下不允许重新加载。
这是真正的动态类重新加载发生的地方。我对 JVM 的内部结构不是很熟悉,但是使用调试器和 REPL 进行一些工作可以清楚地表明一点:每次需要解析类名时,例如编译使用该类的代码时或当调用Class类的forName
方法,使用Clojure的DynamicClassLoader/findClass
方法。正如您所注意到的,这会在 DynamicClassLoader 的缓存中查找类名,并且可以通过再次运行 deftype
将其设置为指向新类。
请注意教程中您提到的关于重新加载的类是不同类的警告,尽管具有相同的名称,但仍然适用于 Clojure 类:
(deftype T [a b]) ; define an original class named T
(def x (T. 1 2)) ; create an instance of the original class
(deftype T [a b]) ; load a new class by the same name
(cast T x) ; cast the old instance to the new class -- fails
; ClassCastException java.lang.Class.cast (Class.java:2990)
Clojure 程序中的每个顶级表单都会获得一个新的 DynamicClassLoader,用于该表单中定义的任何新类。这不仅包括通过 deftype
和 defrecord
定义的类,还包括 reify
和 fn
定义的类。这意味着上面的 x
的类加载器与新的 T
不同。请注意 @
后面的数字是不同的——每个数字都有自己的类加载器:
(.getClassLoader (class x))
;=> #<DynamicClassLoader clojure.lang.DynamicClassLoader@337b4703>
(.getClassLoader (class (T. 3 4)))
;=> #<DynamicClassLoader clojure.lang.DynamicClassLoader@451c0d60>
但只要我们不定义新的 T
类,新实例就会具有相同的类和相同的类加载器。请注意,此处 @
后面的数字与上面第二个相同:
(.getClassLoader (class (T. 4 5)))
;=> #<DynamicClassLoader clojure.lang.DynamicClassLoader@451c0d60>
关于clojure - Clojure 类重载是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7471316/
为什么该语言的名称是“Clojure”? 我用谷歌搜索了一下,在#clojure 中询问。到目前为止,还没有运气。 最佳答案 Rich Hickey(他是 Clojure 的设计者)对此的评论是 wi
我不明白为什么升级后会出现以下编译错误: Compiling addr-verify.core Exception in thread "main" java.lang.NoClassDefFound
我试图将从映射操作返回的(惰性)序列传递给另一个映射操作,以便我可以在第一个序列中查找元素。代码从文本文件(以行/列格式)解析一些足球装置,清理它,然后返回一张 map 。 这是代码: (ns fix
我想过滤一组,例如: (filter-set even? #{1 2 3 4 5}) ; => #{2 4} 如果我使用clojure.core/filter我得到一个不是集合的seq: (filte
(defn hi[](+ 5 6)) (hi) (defn hi[](+ 6 7)) (hi) 你好,我是 clojure 的新手。如上所述,我编写了两个具有相同名称的函数。我们可以在 cloj
我按照这个伪代码递归地将十进制转换为二进制。 findBinary(decimal) if (decimal == 0) binary = 0 else binar
我正在尝试学习 Clojure 并尝试定义这个简单的函数: user=> (defn triple [arg] (* 3 arg)) #'user/triple user=> (triple 1) 3
是->和 ->>宏只是为了使代码更具可读性还是它们还有其他特定功能? 最佳答案 线程优先( -> )和线程最后( ->> )是为了使代码更具可读性。但这已经很重要了! 它允许取消嵌套函数调用(示例取自
我在 http://www.learningclojure.com/2010/11/yet-another-way-to-write-factorial.html 上找到了这个代码,但我不明白 pop
我正在阅读 Programming Clojure 2nd edition,在第 49 页它涵盖了 Clojure 的 for 循环结构,它说它实际上是一个序列理解。 作者建议使用以下代码: (def
Clojure 中有双端队列吗?我的印象是 Clojure 的 PersistentQueue 是单端的(我错了吗?)。我需要能够从队列的任一端删除(即“pop”)和“peek”数据。我所说的双端队列
换句话说,有没有办法在看起来不像 (MACRO arg* ...) 的表单上触发宏扩展? . 举一个假设的例子: (defmacro my-var (do (printf "Using my-va
我很难理解懒惰。 有人能帮我理解为什么我下面的函数不是懒惰的吗 (defn my-red ([f coll] (my-red f (first coll) (rest coll) ))
在 Clojure 核心中决定参数函数顺序的规则是什么(如果有的话)? 类似 map 的函数和 filter期望数据结构作为最后一个 争论。 类似 assoc 的函数和 select-keys期待数据
我在 clojuredocs 上遇到过 completing 函数,但目前没有文档。 你能提供一些例子吗? 最佳答案 completing 用于扩充可能没有具有一元“完成”元数的一元重载的二元归约函数
这个现在支持吗?我能找到的唯一信息是来自维基的示例( https://github.com/clojure/core.match/wiki/Deftype-and-defrecord-matching
我正在关注“Clojure in Action”,对此我感到困惑: (defn with-log [function-to-call log-statement ] (fn [& args
对于下面的代码,箭头是宏还是函数名称中的简单字符? (来自 here) (defn file->map [file] ;; TODO ) 最佳答案 箭头是函数名称的一部分。有一个函数定义,不是
Clojure 的 range函数包含来自 start独家在end (如果提供)。核心库中是否有一个函数可以提供完全包含(开始和结束)的范围? 我发现在某些情况下必须调整最终值的代码 - 例如向下而不
当我尝试从 REPL 运行以下代码时(使用动态记录): (defrecord (symbol "rec2") (vec (map symbol ["f1" "f2"]))) 我收到错误 Compile
我是一名优秀的程序员,十分优秀!