Thursday, July 20, 2006

The double inheritance pattern in Java

Java is a single inheritance language, yes? Generics adds a new wrinkle to that, however. Consider this problem:

You have a class hierarchy you wish to wrap. For instance, you want wrapped versions of JTextComponent and its subclasses which implement additional functionality or default behaviors. So the first wrapping looks like this:

public class MyTextComponent
        extends JTextComponent {
    // ...
}

The second wrapping looks like this:

public class MyTextField
        extends JTextField or MyTextComponent {
    // ...
}

Oops! What should the second wrapper extend?

Generics provides a neat, if wordy, solution for this problem. Have the wrapper hierarchy extend only within itself, and have generic types extend the wrapped hierarchy. The wrapper hierarchy delegates to the wrapped one. Thus:

public class LabeledTextComponent<F extends JTextComponent>
        extends Box { // Box is just illustrative
    protected final F field;

    private final JLabel label;

    protected LabeledTextComponent(final F field,
            final String labelText) {
        super(BoxLayout.LINE_AXIS);

        this.field = field;
        label = new JLabel(labelText, SwingConstants.TRAILING);

        label.setLabelFor(field);
        
        add(label);
        add(field);
    }

    // ... public constructors

    // delegates via field specific to JTextComponent

    public String getText() {
        return field.getText();
    }

    public void getText(final String text) {
        field.setText(text);
    }

    // ... other delegates

    // methods specific to LabeledTextComponent

    public JLabel getLabel() {
        return label;
    }
}

public class LabeledTextField<F extends JTextField>
        extends LabeledTextComponent<F> {
    protected LabeledTextField(final F field,
            final String labelText) {
        super(field, labelText);
    }

    public LabeledTextField(final String labelText) {
        super(new JTextField(), labelText);
    }

    public LabeledTextField(final int columns,
            final String labelText) {
        super(new JTextField(columns), labelText);
    }

    // ... other public constructors, etc.
}

And a further example:

public class LabeledPasswordField<F extends JPasswordField>
        extends LabeledTextField<F> {
    protected LabeledTextField(final F field,
            final String labelText) {
        super(field, labelText);
    }

    // ... public constructors

    // delegates via field specific to JPasswordField

    public char[] getPassword() {
        return field.getPassword();
    }

    public void getPassword(final char[] password) {
        field.setPassword(password);
    }

    // ... other delegates
}

Generics combined with delegates simulates a kind of multiple inheritance similar to mixins. One can extend the idea one step further:

public interface Required {
    /** Checks if the field requires text to be valid. */
    boolean isRequired();

    /** Toggles that the field requires text to be valid. */
    void setRequired(final boolean required);

    /** Checks if the field is required and if the text is missing. */
    boolean isMissing();
}

public class RequiredTextField extends JTextField
        implements Required {
    private boolean required;

    // Required
    public boolean isRequired() {
        return required;
    }

    public void setRequired(final boolean required) {
        this.required = required;
    }

    public boolean isMissing() {
        return required
                && null != getText();
                && getText().length() > 0;
    }
}

public class RequiredLabeledTextField
        <F extends JTextField & Required>
        extends LabeledTextField<F>
        implements Required {
    protected RequiredLabeledTextField(final F field,
            final String labelText) {
        super(field, labelText);
    }

    // Required
    public boolean isRequired() {
        return field.isRequired();
    }

    public void setRequired(final boolean required) {
        field.setRequired(required);
    }

    public boolean isMissing() {
        return field.isMissing();
    }
}

Now it is simple to glue it all together cleanly, maintaining separation of concerns and working through interfaces based on capabilities rather than concrete classes.

Generics to the rescue!

NB — I relied heavily on my background in C++ templates for conceptualizing this pattern. But I believe it likely I am treading no new ground. If you know an earlier or better reference for this pattern, please drop me a line. I want to give credit to where it is due.

Post a Comment