import java.util.Vector; import javax.swing.JButton; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; // anonymous inner classes class NPresses { int objMax; NPresses(int n) { objMax = n; } void addToButton(JButton b, final int buttonMax) { b.addActionListener( new ActionListener() { int m = 0; public void actionPerformed(ActionEvent e) { ++m; if(m==objMax) System.out.println("that is enough presses (obj)!"); else if(m==buttonMax) System.out.println("that is enough presses (button)!"); } } ); } } // did we "need" an anonymous class? // No: could use an inner class // No: could use a top-level class and share an object holding objMax // (and a second field for buttonMax) // why is final required for buttonMax? // * So compiler can stack-allocate the parameter, build a closure by // _copying_ the value, and nobody can tell there isn't sharing // * If stack-allocation of parameters is considered an implementation // detail, then this is a somewhat ugly source-level restriction // * But there are straightforward workarounds: use an Integer object; // the fields of an object bound to a final parameter are still mutable. // manual closures interface ClosedCode { Object m(Vector env, Object arg); } class Closure { Vector env; ClosedCode code; Closure(Vector _env, ClosedCode _code) { env = _env; code = _code; } Object apply(Object arg) { return code.m(env,arg); } } class A implements ClosedCode { public Object m(Vector env, Object arg) { return (String)env.get(0) + arg.toString(); // + is string concatenation } Closure make_closure() { Vector v = new Vector(); v.add(0,"arg is: "); return new Closure(v, this); } } // in other words, we can program by hand using ideas behind closure conversion // it's painful and error-prone: // Any concrete method m will have to access the env and downcast the // results. // anonymous inner classes work out a bit better for simple cases abstract class Closure2 { abstract Object apply(Object arg); } class A2 { Closure2 make_closure() { final String s = "arg is: "; return new Closure2() { Object apply(Object arg) { return s + arg.toString(); } }; } }