2014-04-29 10 views
6

Gram w Clojure (1.6) i JavaFX 8, i od samego początku napotkałem problem. Na przykład, to bardzo prosty kod zawiedzie:Clojure nie może importować klas JavaFX za pomocą inicjalizatorów statycznych.

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

Jest to ostatnia część śladu stosu, który wydaje stosowne:

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 

nie mówię Java w ogóle, ale badanie to wydaje że problem leży w Clojure i sposobie, w jaki importuje klasy Java. Jeśli rozumiem poprawnie, w czasie importu uruchamia inicjalizator statyczny klasy, a dla niektórych klas JavaFX (Button w moim przypadku) ulega awarii.

Zgadnij Mam dwa pytania: czy moje zrozumienie tego błędu jest prawidłowe? Po drugie, czy istnieje jakiś sposób obejścia tego problemu? Próbowałem ciągnięcie importu wewnątrz funkcji zamiast na deklaracji (ns), ale nadal nie działa.

Jeśli nie ma poprawki Clojure, czy można to naprawić za pomocą dodatkowego kodu Java?

Wszelkie wskazówki i wskazówki są mile widziane!

Odpowiedz

3

Nie mogłem znaleźć sposobu na zmianę zachowania importu Clojure, ale znalazłem kilka hacków, aby zrobić to, czego potrzebuję.

Po pierwsze, JavaFX udostępnia klasy budowniczych, więc najczystszym sposobem w tym przypadku byłoby użycie ButtonBuilder do utworzenia nowych przycisków.

Drugim sposobem byłoby napisanie prostej klasy Javy, która owija kod Button, a następnie po stronie Clojure importować tę klasę owijania. Jest to dobre rozwiązanie w przypadku pracy z mniejszą liczbą problematycznych klas.

Trzeci sposób byłoby importować przy starcie, coś jak to (dzięki chłopaki na #clojure za pomoc z tym):

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

W końcu wydaje się to brzydkie brodawki w Javie Clojure za współdziałanie, byłoby wspaniale, gdyby można było to naprawić w przyszłości.


UPDATE: Jest też clojure.lang.RT/classForNameNonLoading, ale niestety, to nie jest tak public z Clojure 1.6. Jest to łatwe do ponownego wprowadzenia jej w życie w Clojure, choć:

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

Później klasa może być tworzony z clojure.lang.Reflector/invokeConstructor.