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.
2 comments:
The problem with this type of system in java is the "weird" debugging issues you get. That is why the decorator design pattern (that you invented ;-P ) is not used much on the ui layer.
Yes and no. :)
My post title referred to the Java-specific detail of having inheritance in the wrapper hierarchy in parallel to inheritance of the wrapped hierarchy. Java generics adds a twist to this.
Post a Comment