即使它们是最终的(final),也可以为 defrecord 的字段设置 set!。
(defprotocol SetA (seta [x a]))
=> SetA
(defrecord X [a]
SetA
(seta [this newa]
; 下一条语句应该在编译时出错,但实际上不会出错。
; 然而 (set! a newa) 在编译时正确出错。
(set! (.a this) newa)))
=> user.X
(def x (->X 0))
=> #'user/x
x
=> #user.X{:a 0}
(seta x 1) ;; 这不应运行。
=> 1
x
=> #user.X{:a 1}
这里有两个问题
# Clojure 编译器没有检测到 (set! (.a this) x) 是对最终字段的赋值。这可以通过增强来改进。Nicola Mometto 已经发现了原因,并相信他有一个简单的补丁。
# JVM 字节码验证器仅在类文件版本 9 及以上版本上执行必要的最终赋值检查:
https://bugs.openjdk.java.net/browse/JDK-8159215 (Clojure 生成版本 6 的类文件。) 这不在我们手中。
方法:如果尝试设置最终字段的 set!,则在编译时让编译器失败。
补丁:0001-CLJ-2126-don-t-assign-final-fields.patch