The Source for Java Technology Collaboration
User: Password:



Evan Summers

Evan Summers's Blog

Better than Good

Posted by evanx on October 11, 2006 at 04:11 AM | Comments (6)

Thanks to comments to The Good, The Bad and The Ugly, i was taught that closures have important features, like access to non final variables in scope.

This does simplify code as follows (see Event DTs), where otherwise arguments all have to be final, and variables like option can't be local in showConfirmDialog().

public Class EdtHelper {
    ...
    int option; // not needed, yay
    public int showConfirmDialog(String message) {
        int option;
        invokeAndWait(Runnable() { 
            option = JOptionPane.showConfirmDialog(null, message, null, 
                JOptionPane.YES_NO_OPTION);
        });
        return option;
    }

    public void showExceptionDialog(Exception e, String message) {            
        if (message == null) message = "D'oh!";
        invokeAndWait(Runnable() { 
            JOptionPane.showMessageDialog(null, 
                new String[] {message, e.getMessage()}, 
                null, JOptionPane.OK_OPTION);
        });
    }

    public void setEnabled(Component component, boolean enabled) {
        invokeAndWait(Runnable() { 
            component.setEnabled(enabled);
        });
    }

    public void requestFocusInWindow(Component component) {
        SwingUtilities.invokeLater(Runnable() { 
            component.requestFocusInWindow();
        });
    }
    ...
    public void invokeAndWait(Runnable runnable) {
        if (!SwingUtilities.isEventDispatchThread()) {
            SwingUtilities.invokeAndWait(runnable);
        } else {
            runnable.run();
        }
    }
   
    public void setWaitCursor(boolean waitCursor) {
        invokeAndWait(Runnable() {
            Cursor cursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
            if (!waitCursor) cursor = null;
            applicationFrame.setCursor(cursor);
            if (glassPane != null) glassPane.setVisible(waitCursor);
        });
    }

    public void startWorker(Runnable runnable) {
        SwingWorker swingWorker = new SwingWorker() {
            public Object construct() throws Exception {
                setWaitCursor(true);
                try {
                    runnable.run();
                } catch (Exception e) {
                    showExceptionDialog(e, null);
                } finally {
                    setWaitCursor(false);
                }
                return null;
            }
        };
        swingWorker.start();
    }
}

Then starting long tasks using above EDT-agnostic support methods would be quite neat, and we can use non final variables in scope, eg. taskDescription.

    refreshButton.addActionListener(ActionListener(ActionEvent e) {         
        String taskDescription = "Refresh data from server";
        edtHelper.startWorker(Runnable() {
            try {
                ... // long task
                if (!edtHelper.showConfirmDialog(
                    "Oops, timeout! You wanna try another server?")) {
                    return;
                }
                ...
            } catch (Exception e) {
                edtHelper.showExceptionDialog(e, taskDescription);
            }
        });
    }
}

So potentially very nice and useful.

But if closures can have access to non final variables in scope, then why can't anonymous classes just get them first? I mean, is that all closures offer compared to anonymous classes? That is apart from saving keystrokes, which i don't think is terribly important, because IDEs type those for us anyway, as argued in my original blog - and that arguably it's harmful because it reduces clarity as to the relevant method, and provides two syntaxes for the same thing.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • If the definition of a closure in a method body can access the local variables of the method, and then the closure is supplied as a return type for the method (is this possible?), does that enable local variables to "leak" outside of their declaring scope?

    Posted by: chris_e_brown on October 11, 2006 at 04:35 AM


  • Yes, that's exactly what that means. That is the essence of a Closure, a construct that captures it environment.

    Mind, those local variables are not "exposed" in anyway, you can't access them (save through the code in the closure itself).

    This is, however, ovarall a Good Thing, since the state of the closure travels with it.


    Posted by: whartung on October 11, 2006 at 02:35 PM

  • whartung,

    that is what I essentially dislike. What, if I define two closures in sequence? They both travel with their environment both eventually influencing each other. In that respect local variables may have a comparabale scope as instance variables (and consequently must be placed on the heap). This becomes potentially dangerous if the closures will be used in different threads. For that case its totally unclear to me how synchronization will take place.

    Another point not mentioned here (I dislike either), is non-local return, which could be realized in anonymous classes only with exceptions.

    Posted by: rbirenheide on October 12, 2006 at 12:11 AM

  • Non-local return rocks for synchronous and pseudo-synchronous (like invokeAndWait) closures. That is, I want to run it now under someone else's control but within the context of this method and its control flow. I think that "if" and "for" loops are easy for people to understand. Really. Same thing here only it allows for reliable ways of getting on other threads, closing files, closing connections, synchronizing, or whatever else might be needed.
    As for just allowing write access to local variables from traditional anonymous inner classes, sure that could be implemented. As an experiment, I tweaked the Eclipse compiler to do just that. (Note that I'm not currently actively working on the project, though.)

    Posted by: tompalmer on October 12, 2006 at 09:43 AM

  • Essentially, you have no guarantee (unless the API gives one) that there is synchronous execution. The API might store the closure for later call (essentially the case for any listener). Anyway, yes, for synchronous calls it might be useful and that essentially is what I personally use anonymous classes for, if I use them at all.

    tompalmer,

    Same thing here only it allows for reliable ways of getting on other threads, closing files, closing connections, synchronizing, or whatever else might be needed.

    The meaning of this sentence is not clear to me. Could you explain?

    Posted by: rbirenheide on October 12, 2006 at 11:50 PM

  • Meaning that when you can create your own code blocks, you can make blocks that automatically close streams when the block exits. Or that synchronize on objects (perhaps in ways other than the normal synchronize block) during the scope of the block. And normal blocks in Java are easier to use than anonymous inner classes (because of things like break and return working).
    I was just giving examples of things that are hard to program correctly in Java (and yes that includes closing resources) that would be easier to do with arbitrary closure support. They are very handy things with lots of applications.

    Posted by: tompalmer on October 16, 2006 at 03:11 PM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds