我正在将一个实例绑定到Var:
(ns org.jb
(:import (java.awt PopupMenu
TrayIcon
Toolkit
SystemTray)
(javax.swing JFrame
Action)))
(def ^:dynamic popupmenu)
(def ^:dynamic image)
(def ^:dynamic trayicon)
(def ^:dynamic tray)
(defn start-app [appname icon]
(binding [popupmenu (new PopupMenu)
image (.. Toolkit (getDefaultToolkit) (getImage icon))
trayicon (new TrayIcon image appname popupmenu)
tray (. SystemTray getSystemTray)]
(. trayicon setImageAutoSize true)
(. tray add trayicon)))
(start-app "escap" "res/escap_icon.png")
错误:
ClassCastException clojure.lang.Var$Unbound cannot be cast to java.awt.Image org.jb/start-app (org\jb.clj:17)
我正在使用
预定义Var(def image)
甚至尝试过
(def ^:dynamic image)
无法理解消息的期望。
然而,在词法范围内使用let代替绑定工作。但是想要实现动态绑定。
答案 0 :(得分:7)
我在这里看到的是一个没有代码的空binding
表单。离开binding
表单后,变量绑定超出范围。根据您的错误消息,您似乎正在尝试在绑定表单之外使用image
var。您需要确保使用image
的所有代码都放在绑定中。
所以,而不是:
(binding [*image* (.. Toolkit (getDefaultToolkit) (getImage "icon.png"))])
(display-image *image*)
这样做:
(binding [*image* (.. Toolkit (getDefaultToolkit) (getImage "icon.png"))]
(display-image *image*))
另一个可能的问题是绑定表达式是并行,而let
中的表达式是按顺序计算的。这意味着如果您绑定多个vars而另一个取决于另一个,它将使用范围内的值之前评估绑定。
所以,这会引发异常:
(def ^:dynamic *a*)
(def ^:dynamic *b*)
(binding [*a* 2
*b* (+ *a* 3)]
(+ *a* *b*)) ; => ClassCastException clojure.lang.Var$Unbound cannot be cast
; to java.lang.Number clojure.lang.Numbers.multiply
; (Numbers.java:146)
相反,您必须使用嵌套的绑定表单:
(binding [*a* 2]
(binding [*b* (+ *a* 3)]
(+ *a* *b*))) ; => 8
请注意,我在var名称周围放置了“耳罩”。这是Clojure中动态变量的命名约定,因此其他人可以很容易地说出它是动态的。此外,如果您能够动态绑定var而不使用^:dynamic
元数据声明它,那意味着您正在使用相当旧版本的Clojure。我建议你升级 - 1.5.1是最新的稳定版本。
答案 1 :(得分:0)
在您的示例中使用binding
毫无意义。仅当您要重新绑定全局变量以创建某些上下文时,才应使用binding。在您的情况下,您不需要全局变量,因此您应该使用let代替:
(ns org.jb
(:import (java.awt PopupMenu
TrayIcon
Toolkit
SystemTray)
(javax.swing JFrame
Action)))
(defn start-app [appname icon]
(let [popupmenu (new PopupMenu)
image (.. Toolkit (getDefaultToolkit) (getImage icon))
trayicon (new TrayIcon image appname popupmenu)
tray (. SystemTray getSystemTray)]
(. trayicon setImageAutoSize true)
(. tray add trayicon)))
(start-app "escap" "res/escap_icon.png")
但是,如果您决定坚持使用binding
,那么Alex's answer shuld可以帮助您解决问题。
但是除非绝对必要,否则你应该避免使用像binding
这样的东西。
如果您的目标是保存某些状态以供将来计算,那么binding
将无法为您提供帮助。它只将变量与其体内的新值绑定在一起,使其在应用程序的其余部分保持不变。
因此,当您想要更改全局状态时,应使用alter-var-root代替:
(def ^:dynamic *app-state* {})
(defn set-state! [new-state]
(alter-var-root #'*app-state* (constantly new-state)))
(defn update-state! [mixin]
(alter-var-root #'*app-state* merge mixin))
你也应尽量保持你的大部分功能尽可能接近“功能范例”:
(defn start-app
"Creates new app with given appname and icon and returns it"
[appname icon]
(let [popupmenu (new PopupMenu)
image (.. Toolkit (getDefaultToolkit) (getImage icon))
trayicon (new TrayIcon image appname popupmenu)
tray (. SystemTray getSystemTray)]
(. trayicon setImageAutoSize true)
(. tray add trayicon)
{ :popupmenu popupmenu
:image image
:trayicon trayicon
:tray }))
(update-state! (start-app "escap" "res/escap_icon.png"))