此错误最明显的症状是在类路径中有一个名为 'w' 的类(默认包)(这些类由如 yFiles 等混淆工具生成),然后尝试加载 Clojure 的核心类。例如
java -cp hotspotapi.jar:clojure-1.4.0-slim.jar clojure.main
(其中 hotspotapi.jar 是一个典型的混淆 JAR 示例)的结果是
线程 "main" 中的异常 java.lang.ExceptionInInitializerError
at clojure.main.<clinit>(main.java:20)
原因:java.lang.NoSuchFieldException: close, 编译:(clojure/core.clj:6139)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6462)
at clojure.lang.Compiler.analyze(Compiler.java:6262)
at clojure.lang.Compiler.analyze(Compiler.java:6223)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5618)
at clojure.lang.Compiler$TryExpr$Parser.parse(Compiler.java:2178)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6455)
at clojure.lang.Compiler.analyze(Compiler.java:6262)
at clojure.lang.Compiler.analyze(Compiler.java:6223)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5618)
at clojure.lang.Compiler$LetExpr$Parser.parse(Compiler.java:5919)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6455)
at clojure.lang.Compiler.analyze(Compiler.java:6262)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6443)
at clojure.lang.Compiler.analyze(Compiler.java:6262)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6443)
at clojure.lang.Compiler.analyze(Compiler.java:6262)
at clojure.lang.Compiler.analyze(Compiler.java:6223)
at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5618)
at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5054)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3674)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6453)
at clojure.lang.Compiler.analyze(Compiler.java:6262)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6443)
at clojure.lang.Compiler.analyze(Compiler.java:6262)
at clojure.lang.Compiler.access$100(Compiler.java:37)
at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:518)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6455)
at clojure.lang.Compiler.analyze(Compiler.java:6262)
at clojure.lang.Compiler.analyze(Compiler.java:6223)
at clojure.lang.Compiler.eval(Compiler.java:6515)
at clojure.lang.Compiler.load(Compiler.java:6952)
at clojure.lang.RT.loadResourceScript(RT.java:359)
at clojure.lang.RT.loadResourceScript(RT.java:350)
at clojure.lang.RT.load(RT.java:429)
at clojure.lang.RT.load(RT.java:400)
at clojure.lang.RT.doInit(RT.java:436)
at clojure.lang.RT.<clinit>(RT.java:318)
... 1 more
原因:java.lang.NoSuchFieldException: close
at java.lang.Class.getField(Class.java:1537)
at clojure.lang.Compiler$StaticFieldExpr.<init>(Compiler.java:1180)
at clojure.lang.Compiler$HostExpr$Parser.parse(Compiler.java:923)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6455)
... 37 more
找不到主类:clojure.main。程序将退出。
为了理解发生的事情,请考虑以下简单的测试
import java.io.StringReader;
import clojure.lang.Compiler;
import clojure.lang.RT;
public class Test {
public static void main(String(link: ) args) {
RT.var("clojure.core", "require");
String s = "(let (link: mumble (new java.io.StringReader \"\")) (. mumble close))";
Compiler.load(new StringReader(s));
}
}
这应当很明显,点操作符中的 'mumble' 引用了局部定义的 mumble。然而,如果我们定义名为 'mumble' 的类在默认包中,Clojure 会选择那个。
为了避免任何反对意见:是的,我们知道将类放在默认包中是非常糟糕的形式。关键在于,Java 生态系统非常多样化,有许多 JAR 文件人们可能无法控制。虽然有人可能会争辩,“不要在默认命名空间中放置类”,关键在于,Clojure 在这里是错误的,这些情况在实际情况中确实会发生,并非由于实现者的错误。