Clojure无法使用静态初始值设定项导入JavaFX类

kme*_*lvn 6 javafx clojure javafx-8

我正在玩Clojure(1.6)和JavaFX 8,一开始我遇到了问题.例如,这个非常简单的代码失败了:

(ns xxyyzz.core)

(gen-class :name "xxyyzz.core.App"
           :extends javafx.application.Application
           :prefix "app-")

(defn app-start [app stage]
  (let [button (javafx.scene.control.Button.)]))

(defn launch []
  (javafx.application.Application/launch xxyyzz.core.App (into-array String [])))

(defn -main []
  (launch))
Run Code Online (Sandbox Code Playgroud)

这是堆栈跟踪的最后一部分似乎相关:

Caused by: java.lang.ExceptionInInitializerError
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:340)
        at clojure.lang.RT.classForName(RT.java:2070)
        at clojure.lang.Compiler$HostExpr.maybeClass(Compiler.java:969)
        at clojure.lang.Compiler$HostExpr.access$400(Compiler.java:747)
        at clojure.lang.Compiler$NewExpr$Parser.parse(Compiler.java:2494)
        at clojure.lang.Compiler.analyzeSeq(Compiler.java:6560)
        ... 48 more
Caused by: java.lang.IllegalStateException: Toolkit not initialized
        at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:276)
        at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:271)
        at com.sun.javafx.application.PlatformImpl.setPlatformUserAgentStylesheet(PlatformImpl.java:562)
        at com.sun.javafx.application.PlatformImpl.setDefaultPlatformUserAgentStylesheet(PlatformImpl.java:524)
        at javafx.scene.control.Control.<clinit>(Control.java:81)
        ... 55 more
Run Code Online (Sandbox Code Playgroud)

我根本不会说Java,但研究它似乎问题在于Clojure及其导入Java类的方式.如果我理解正确,在导入时它会运行类静态初始化程序,并且对于某些Button崩溃的JavaFX类(在我的例子中).

猜猜我有两个问题:我对这个错误的理解是否正确?第二,有办法以某种方式解决这个问题吗?我尝试在(ns)声明中提取函数内部的导入,但它仍然不起作用.

如果没有Clojure修复,是否可以通过一些额外的Java代码修复?

欢迎任何提示和指示!

kme*_*lvn 3

我找不到改变 Clojure 导入行为的方法,但我确实找到了一些技巧来完成我需要的操作。

首先,JavaFX 提供了构建器类,因此在这种特殊情况下最干净的方法是用于ButtonBuilder创建新的按钮。

第二种方法是编写一个简单的 Java 类来包装Button,然后从 Clojure 端导入该包装类。当处理少量有问题的类时,这是一个不错的解决方案。

第三种方法是在运行时导入,如下所示(感谢 #clojure 的人员帮助解决此问题):

(defn import-at-runtime [name]
  (.importClass (the-ns *ns*)
                (clojure.lang.RT/classForName name)))

(import-at-runtime "javafx.scene.control.Button")

(let [button (eval `(new ~(symbol "javafx.scene.control.Button") ~"Button Text"))
Run Code Online (Sandbox Code Playgroud)

最后,这看起来像是 Clojure 的 Java 互操作中的一个丑陋的疣,如果将来能够修复它那就太好了。


更新:还有clojure.lang.RT/classForNameNonLoading,但不幸的是,它不是publicClojure 1.6 的。不过,在 Clojure 中重新实现它很容易:

(fn [^String class-name]
  (Class/forName class-name false (clojure.lang.RT/baseLoader)))
Run Code Online (Sandbox Code Playgroud)

稍后,可以使用 实例化该类clojure.lang.Reflector/invokeConstructor