【问题标题】:Clojure can't import JavaFX classes with static initializersClojure 无法使用静态初始化程序导入 JavaFX 类
【发布时间】:2014-04-29 13:00:54
【问题描述】:

我在玩 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))

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

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

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

我想我有两个问题:我对这个错误的理解正确吗?其次,有没有办法以某种方式解决这个问题?我已经尝试在 (ns) 声明中拉取函数内部的导入,但它仍然不起作用。

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

欢迎任何提示和指点!

【问题讨论】:

    标签: clojure javafx javafx-8


    【解决方案1】:

    我找不到改变 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"))
    

    最后,这似乎是 Clojure 的 Java 互操作中的一个丑陋的疣,如果将来可以修复它会很棒。


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

    (fn [^String class-name]
      (Class/forName class-name false (clojure.lang.RT/baseLoader)))
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 2013-10-23
      • 1970-01-01
      • 2020-04-14
      • 2018-07-05
      相关资源
      最近更新 更多