Clojure无法使用静态初始值设定项导入JavaFX类 [英] Clojure can't import JavaFX classes with static initializers

查看:184
本文介绍了Clojure无法使用静态初始值设定项导入JavaFX类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm playing around with Clojure (1.6) and JavaFX 8, and right at the start I've run into a problem. For example, this very simple code fails:

(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))

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

This is the last part of the stack trace that seems relevant:

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 )崩溃。

I don't speak Java at all, but researching this it seems that the problem lies with Clojure and the way it imports Java classes. If I understand correctly, at import time it runs the class static initializer, and for some JavaFX classes (Button in my case) that crashes.

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

Guess I have two questions: is my understanding of this error correct? And second, is there a way to work around this problem somehow? I've tried pulling the imports inside functions instead at the (ns) declaration, but it still doesn't work.

如果没有Clojure修复,可能这可能修复了一些额外的Java代码?

If there's no Clojure fix, could this be possibly fixed with some additional Java code?

欢迎任何提示和指示!

推荐答案

我找不到改变Clojure导入行为的方法,但我确实找到了几个黑客来做我需要的东西。

I couldn't find a way to alter Clojure's import behavior, but I did find a couple of hacks to do what I need.

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

First, JavaFX provides builder classes, so the cleanest way in this particular case would be to use ButtonBuilder to create new Buttons.

第二方法是编写一个包装 Button 的简单Java类,然后从Clojure的那边导入包装类。当处理少量有问题的类时,这是一个很好的解决方案。

Second way would be to write a simple Java class that wraps the Button, and then from Clojure's side import that wrapping class. It's an OK solution when working with smaller number of problematic classes.

第三种方式是在运行时导入,就像这样(感谢#clojure的帮助这个):

Third way would be to import at runtime, something like this (thanks to guys at #clojure for helping out with this):

(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互操作中看起来像一个丑陋的疣,如果将来可以修复它会很棒。

In the end, this seems like a ugly wart in Clojure's Java interop, it would be great if it could be fixed in the future.

更新:还有 clojure.lang.RT / classForNameNonLoading ,但不幸的是,从Clojure 1.6开始,它不是 public 。这很容易在Cloju重新实施它但是:

UPDATE: There's also clojure.lang.RT/classForNameNonLoading, but unfortunately, it's not public as of Clojure 1.6. It's easy to re-implement it in Clojure, though:

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

稍后,该类可以用<$ c实例化$ c> clojure.lang.Reflector / invokeConstructor 。

这篇关于Clojure无法使用静态初始值设定项导入JavaFX类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆