2014-04-29 13 views
6

Clojure (1.6) ve JavaFX 8 ile oynuyorum ve başlangıçta bir sorunla karşılaşıyorum.Clojure, statik başlatıcılar ile JavaFX sınıflarını içe aktaramaz

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

Bu alakalı görünüyor yığın izlemesi son bölümüdür: Örneğin, bu çok basittir kod başarısız

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 

ben hiç Java bilmeyen, fakat bu araştırma görünüyor Sorun Clojure ve Java sınıflarını içe aktarma biçiminde yatıyor. Doğru olarak anlarsam, içe aktarma sırasında sınıf statik başlatıcısını ve bazı JavaFX sınıflarını (benim durumumda Button) çöker.

Sanırım iki sorum var: Bu hatayı anlamak benim için doğru mu? Ve ikincisi, bu soruna bir şekilde çözüm bulmak için bir yol var mı? İthalatı (ns) beyanında yerine işlevlerin içine çekmeyi denedim, ancak hala çalışmıyor.

Clojure düzeltmesi yoksa, bu, bazı ek Java kodlarıyla düzeltilebilir mi?

Herhangi bir ipucu ve işaretçiler kabul edilir!

cevap

3

Clojure'un içe aktarma davranışını değiştirmenin bir yolunu bulamadım, ancak ihtiyacım olanı yapmak için birkaç tane hack buldum.

İlk olarak, JavaFX oluşturucu sınıfları sağlar, bu nedenle bu durumda en temiz yol, yeni Düğmeler oluşturmak için ButtonBuilder'u kullanmak olacaktır.

İkinci yol, Button adlı sarılmış basit bir Java sınıfı yazmak ve sonra da Clojure tarafından bu sınıf sınıfını içe aktarmak olacaktır. Daha az sayıdaki sorunlu sınıfla çalışırken bu iyi bir çözümdür.

Üçüncü bir yol (bununla yardımın için #clojure de çocuklar sayesinde çalışma zamanında), böyle bir şey ithal etmek olacaktır: Sonunda

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

, bu Clojure en Java çirkin siğil gibi görünüyor interop, gelecekte düzeltilebilirse harika olurdu.


GÜNCELLEME: Orada clojure.lang.RT/classForNameNonLoading da vardır, ancak ne yazık ki, Clojure 1.6 itibariyle değil public bu. Yine de Clojure içinde yeniden uygulamak kolaydır:

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

Daha sonra sınıf clojure.lang.Reflector/invokeConstructor ile örneği olabilir.