Back when closures were first explained to me, a long time ago, I thought “sounds like a language with pass-by-reference semantics like Pascal.” Of course, it isn’t quite that simple.
Clojure has a lot of nice features that work naturally to give you a “better Java than Java”. Here’s an example of using a closure that is not at all easy in Java.
(ns net.dneclark.JFrameAndTimerDemo
(:import (javax.swing JLabel JButton JPanel JFrame Timer))
(:gen-class))
(defn timer-action [label counter]
(proxy [java.awt.event.ActionListener] []
(actionPerformed
[e]
(.setText label (str "Counter: " (swap! counter inc))))))
(defn timer-fn []
(let [counter (atom 0)
label (JLabel. "Counter: 0")
timer (Timer. 1000 (timer-action label counter))
panel (doto (JPanel.)
(.add label))]
(.start timer)
(doto (JFrame. "Timer App")
(.setContentPane panel)
(.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
(.setLocation 300 300)
(.setSize 200 200)
(.setVisible true)))
(println "exit timer-fn"))
(defn -main []
(timer-fn))
If you compile and run the program, you will see a small window with a counter than increments every second. You will also see a message displayed that the function timer-fn
has exited. Big deal, huh?
But look at the declaration of the counter. It’s in the definition of the timer-fn
. It isn’t a global. But the action listener, timer-action
, still has access to the variable and continues to increment it – even though the function that declared the variable has completed execution. The lexical context of that variable is maintained and the action listener has access to it. Very neat.
I don’t know if Java will ever get closures, but it sure is simple to use them in Clojure.