Tag Archives: Swing

Java Does Not Need Closures. Not at All.

I hope that most Java developers are aware of the Swing threading policy. It makes sense, except for that it’s so insanely hard to follow. And if you do follow it, I dare say there is no place with more ridiculous boilerplate.

Here’s two examples that recently got me scratching my head, gnashing my teeth, and eventually weep in disdain.

Case One: Display Download Progress

class Downloader {
    download() {
        progress.startDownload();
        while(hasMoreChunks()) {
            downloadChunk();
            progress.downloadedBytes(n);
        }
        progress.finishDownload();
    }
}

class ProgressDisplay extends JPanel {
    JLabel label;
    startDownload() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                label.setText("Download started");
            }
        });
    }
    downloadedBytes(int n) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                label.setText("Downloaded bytes: " + n);
            }
        });
    }
    finishDownload() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                label.setText("Download finished");
            }
        });
    }
}

Solution? Easy! Just write your own JLabel wrapper to hide this mess. Then a lengthy JTable utility. Then a JProgressBar wrapper. Then…

Case Two: Persist Component State

Task: Save JTable state to disk. You’ve got to read table state in EDT, but writing to disk from EDT is not the the best idea.

Solution:

ExecutorService executor = Executors.newSingleThreadExecutor();

void saveState(final Table table) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            TableColumns state = dumpState(table);
            executor.execute(new Runnable() {
                public void run() {
                    saveToDisk(table.getTableKey(), state);
                }
            });
        }
    });
}

Two inner classes, inheritance, all those publics and voids and parentheses. And there is no way to avoid that!

In a sane language, it could look like:

(defn save-state [table]
  (do-swing
    (let [state (dump-state table)]
      (future (save-to-disk table state)))))

All semantics. Little ceremony, almost no syntax. If you counted characters in both solutions, I guess the entire second snippet is as long as the first one somewhere to the invocation of invokeLater. Before you get to the actual logic. No strings attached, do-swing and future are provided by the language or “official” utils.

Bottom Line

I recently saw two presentations by Venkat Subramaniam on polyglot programming where he mercilessly made fun of Java. In his (vaguely remembered) words, when you write such code for the whole day, you come back home and weep. You can’t sleep because you’re tormented by the thought of the abomination you created, and that you will have to deal with tomorrow. If your children asked you what do you for living, would like them to see this?

Don’t Rely on Autoboxing in Java

Question: What does the following code display?

import java.awt.Color;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;

public class Demo {
  public static void main(String[] args) {
    JFrame frame = new JFrame();
    JLayeredPane container = new JLayeredPane();
    frame.setContentPane(container);

    JPanel bottom = new JPanel();
    bottom.setBounds(0, 0, 400, 200);
    bottom.setBackground(Color.WHITE);
    container.add(bottom); // default level is 0

    JLabel intLabel = new JLabel("I'm an int");
    intLabel.setBounds(0, 10, 400, 30);
    int layer = 10;
    container.add(intLabel, layer);

    JLabel integerLabel = new JLabel("I'm an Integer");
    integerLabel.setBounds(0, 50, 400, 30);
    container.add(integerLabel, new Integer(10));

    JLabel literalLabel = new JLabel("I'm a literal");
    literalLabel.setBounds(0, 100, 400, 30);
    container.add(literalLabel, 10);

    JLabel longLabel = new JLabel("I'm a long");
    longLabel.setBounds(0, 150, 400, 30);
    container.add(longLabel, new Long(10));

    frame.setSize(400, 200);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
  }
}

Answer

I'm an Integer

Explanation

If you open docs for JLayeredPane, you can see the following code snippets:

layeredPane.add(child, new Integer(10));
layeredPaneParent.setLayer(child, 10);

You can see Integer there and literal 10 here. Subconsciously, you think: who cares, 10 is the same thing as new Integer(10) because Java has autoboxing, right? WRONG!

The only right way to interact with layeredPane.add is an Integer that you box yourself. Let’s take a peek at its source code…

protected void addImpl(Component comp, Object constraints, int index) {
    int layer = DEFAULT_LAYER.intValue();
    int pos;

    if(constraints instanceof Integer) { // A-ha!
        layer = ((Integer)constraints).intValue();
        setLayer(comp, layer);
    } else
        layer = getLayer(comp);

    pos = insertIndexForLayer(layer, index);
    super.addImpl(comp, constraints, pos);
    comp.validate();
    comp.repaint();
    validateOptimizedDrawing();
}

Ironically, even though it uses int internally, it wouldn’t recognize it as a parameter.

Conclusion

Autoboxing works, except for when it doesn’t. Be defensive with APIs (even core Java) and when something is wrong do try all those ideas that seem obviously crazy at first. When writing your own API that uses type identification, remember that int and Integer are completely different beasts and both need to be included.

This example is from Swing, but it can happen anywhere. I’ve tripped on this issue before when I was implementing a CRUD framework or renderers, but did not expect it in core library.

Update

As frimble pointed out at reddit, there is one more reason why it would never work. In addition to add(Component comp, Object constraints), Container also has add(Component comp, int index).

Having two overloaded versions of a method, one of them accepting Integer and another for int, when both do completely different things, is even worse than what I criticized in the first place.

Rant: Yet another example of extremely poor design in core Java API. Just like Object.equals() having specification that’s impossible to meet in a reasonable way or the hierarchy of java.util.Date. They use Java to teach OOP. Apparently it’s just as good for teaching how NOT to program.