Leepoint Java Reference Notes on the Java language and its standard library.

Alternatives to writing a Swing GUI by hand

Building a Java interface is not hard. Building one that is easy to maintain and does not look dated is the hard part, and several approaches try to solve it.

Getting a graphical interface working in Java stops being difficult fairly quickly. Getting one that is pleasant to write, survives being maintained, and does not look a decade old is a different problem, and every approach below is an attempt at it.

Separating the interface from the logic

Whatever route you take, this is the decision that matters most, and it is the one a beginner’s program always gets wrong.

The bad shape: the code that performs a button’s action lives inside that button’s listener. It works, and it is how almost every first Swing program is written. It stops working as soon as the program grows: the logic is now scattered across a dozen anonymous classes, reachable only by clicking things, and impossible to test.

The shape that survives: interface in one class, logic in another. The listener does nothing but call a method on the logic class. This costs almost nothing to do from the start and is expensive to retrofit. Some of the tools below encourage it; several actively work against it, which is a fair reason to reject one.

Form designers

A form designer lets you place components visually and generates the Java that builds them. NetBeans’ Matisse is the best known for Swing; the IntelliJ and Eclipse designers work similarly, and there have been many standalone products.

The appeal is real. Laying out a dialog by hand with GridBagLayout is slow and unpleasant, and a designer does it in a minute.

What comes out

Typically a large initComponents() method, marked as generated and often locked against editing, full of GroupLayout constraints:

layout.setHorizontalGroup(
    layout.createParallelGroup(GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addContainerGap()
            ...

GroupLayout is designed to be generated. It expresses a layout precisely and is close to unwritable by hand, which is fine while the tool is in charge, and a problem the moment it is not.

Where it goes wrong

Merge conflicts. Generated layout code is long, dense and changes wholesale when anyone moves a control. Two people editing the same form produces a conflict nobody can resolve by reading it.

Lock-in. The file usually only round-trips in the tool that made it. Change IDE and you inherit code you cannot edit by hand and cannot regenerate.

Logic drifting in. The dialog class becomes the place event handlers live, then validation, then business rules, because the designer put a handler stub there and it was the path of least resistance. That is how a form ends up holding the rules of the application.

Getting the benefit without the cost

Keep generated code alone. Do not hand-edit it. Anything you write there is lost on regeneration.

Handlers delegate immediately. The generated stub should call a method you wrote, and do nothing else:

private void saveButtonActionPerformed(ActionEvent evt) {
    controller.save();
}

Everything real then lives in a class the designer never opens.

Keep state out of the form. Which fields are valid, whether saving is allowed. Put it in a class you can test without constructing a window. See presentation model.

Use the designer for layout only. It is genuinely good at arranging components and it should not be doing anything else.

Describing the interface in markup instead

The other long-running idea is to stop expressing the interface in a programming language at all and describe it in a markup document. The structure of a form is, after all, a tree of components with properties, which is exactly what markup is good at. The program reads the description at startup and builds the widgets from it.

Mozilla’s XUL was the best-known attempt at a general version of this, and Microsoft’s XAML took the same idea considerably further; on the Java side there were several libraries that read an XML file and assembled a Swing interface from it.

The attraction is that separation of interface from logic stops being a matter of discipline and becomes structural: the description cannot contain application code, because it is not code. The recurring problem is tooling and error reporting: a mistake in the markup surfaces at runtime rather than at compile time, and the libraries that did this well enough to depend on were thin on the ground.

The idea did not go away. It is what modern declarative interface toolkits do, and it is worth recognising the lineage when you meet one.

Not using Swing at all

Swing is the standard, not the only option. SWT (written for Eclipse) wraps the platform’s native widgets rather than drawing its own, which historically made it faster and more native-looking at the cost of shipping a platform-specific library. Various lighter toolkits have been built on top of Swing to present a simpler API than Swing’s own.

And there is the question worth asking before any of this: does the interface have to be in Java? A program that already has a server component can put its interface in a browser and avoid the whole subject.

Writing it by hand

Nesting simple managers (a BorderLayout holding BoxLayout panels) is readable, diffs cleanly, and needs no tool. For anything but a dense data-entry form it is competitive on effort and better on maintenance.

For a student program the honest advice is the boring one: write it by hand, keep the logic in its own class, and use a designer only if the form is genuinely tedious to lay out.