Adding fields to a proxied class in Clojure
Asked Answered
I

1

9

I'm using "proxy" to extend various Swing classes in a Clojure GUI application, generally with code that looks something like:

(def ^JPanel mypanel 
  (proxy [JPanel] []
    (paintComponent [#^Graphics g]
      (.drawImage g background-image 0 0 nil))))

This works well but I can't figure out how to add additional fields to the newly extended class, for example making the background-image a field that could be subsequently updated. This would be pretty easy and common practice in Java.

Is there a good way to do this in Clojure? Or is there another preferred method to achieve the same effect?

Irresolution answered 16/6, 2010 at 20:45 Comment(2)
Shameless self-promotion: I wrote a blog post a while ago about proxy and its gotchas.Shaylashaylah
thanks - very useful guide!Irresolution
C
9

You can use something like this:

(defn ^JPanel mypanel [state]
  (proxy [JPanel] []
    (paintComponent [#^Graphics g]
      (do
        (comment do something with state)
        (.drawImage g background-image 0 0 nil)))))

or use any other outer function/ref.

Confined answered 16/6, 2010 at 20:59 Comment(9)
very interesting idea - do you mean with defn rather than def?Irresolution
doto seems appropriate here: (doto g (.drawImage...) (...))Joashus
hmmmm... anyone know how Clojure is implementing this internally? is it actually adding a field to the proxied class?Irresolution
It is defn not def -- with def it wouldn't even compile -- I took the liberty of making the correction. As for the implementation, there is no proxy-specific magic here; method bodies are closures and can refer to whatever is visible in their lexical scope. BTW, method bodies also have an implicit do, so the do above is unnecessary.Rosetta
Incidentally, I really like both the question and this answer. :-)Rosetta
@mikera: no. proxy methods are ye olde clojure functions and hence close over their environment. So the reference to the state is saved in the closure.Shaylashaylah
hmmm so if I understand correctly is extending the JPanel with a paintComponent method that immediately calls an IFn that captures state as an internal private field?Irresolution
@Michal - I agree, very enlightening!Irresolution
yes. that's roughly what happens. There is a map lookup somewhere in between to find the actual clojure function to call, but then things go as you described. (Though I wouldn't describe closed-over values as "internal private field") And it should read "captured" not "captures". The "capture" of the state happens when the proxy is created, not when the function is called.Shaylashaylah

© 2022 - 2024 — McMap. All rights reserved.