给定一个引用类的 Clojure 函数,如果该类有一个在运行时失败的静态字段初始化器,该函数将无法编译,因为编译器(不需要)即使相应的字段未访问也要运行初始化器。值得注意的是,当将类存储在变量中然后通过该变量引用它时,这种情况不会发生(见下面的解决方案)。编译器能否支持这里的直接引用?
重现
(以下代码也可在https://github.com/bevuta/clojure-fn-compilation-issue-repro找到)
deps.edn
:
{:deps {io.netty/netty-codec {:mvn/version "4.1.108.Final"}}}
src/repro/fail.clj
:
(ns repro.fail
(:import io.netty.handler.codec.compression.BrotliOptions))
(defn bar []
BrotliOptions)
(defn -main [& args]
(prn (bar)))
在此处,bar
函数引用了 io.netty.handler.codec.compression.BrotliOptions
类。该类有一个名为 DEFAULT
的包私有静态字段,有一个初始化器。该初始化器依赖于由可选第三方库提供的类。然而,即使用户从未引用过该字段,该初始化器也会运行并失败,因为该库不存在。
运行方式如下:
$ clojure -M -m repro.fail
Execution error (ClassNotFoundException) at jdk.internal.loader.BuiltinClassLoader/loadClass (BuiltinClassLoader.java:641).
com.aayushatharva.brotli4j.encoder.Encoder$Parameters
解决方案
可以通过将类值放置在单独的 def
中,并在函数中使用该值来解决这个问题。
src/repro/ok.clj
:
(ns repro.ok
(:import io.netty.handler.codec.compression.BrotliOptions))
(def foo BrotliOptions)
(defn bar []
foo)
(defn -main [& args]
(prn (bar)))
运行方式如下:
$ clojure -M -m repro.ok
io.netty.handler.codec.compression.BrotliOptions
相关性
在https://github.com/clj-commons/aleph/issues/703的背景下遇到了该问题。