1

I have some functionality in my gui that updates a given text component over time. Ideally, I'd like it to accept anything that has a getText/setText method. This is easily done with JTextField and JTextPane as they share the common parent class JTextComponent (which hold the get/set methods). The issue comes in when I try to include JLabel, as, unfortunately, it's get/setText methods do not come from a common parent class or interface.

To solve this, I see two paths.

1:

create a common interface EditableText, then have my own alias versions of JTextField, JTextPane, and JLabel that implement the interface.

Ala:

public class MyJTextField extends JTextField implements EditableText {

}

My Issue with this is that I'm creating a ton of class bloat just so the compiler knows that I want to access two methods on these classes.

2:

Some kind of template pattern-ish thing that accepts a very general parent class and then casts it up to either a JLabel or JTextComponent.

Something along the lines of:

class JLabelImplementation {
    
    private JLabel label; 

    public MyClass(JComponent componenet) {
        this.label = (JLabel) component
    }
}


class JTextComponentImplementation {
    
    private JTextComponent textField; 

    public MyClass(JComponent componenet) {
        this.textField = (JTextComponent) component
    }
}

(Note: Haven't actually tried the above). So, again, the thing I don't like is that it's still three classes (base functionality + two implementation classes) just to accept an object.

My question is, is there a better way to handle this? Are any of my planned approached good?

Community
  • 1
  • 1
user3308774
  • 1,354
  • 4
  • 16
  • 20
  • I posted [almost the same question](http://stackoverflow.com/questions/22156913/classes-that-defacto-implement-an-interface-but-do-not-declare-the-fact) some time ago. – PM 77-1 Jul 05 '14 at 17:50

2 Answers2

1

There are two ways I can see going about this.

  1. Your first idea isn't bad. The problem is that it seems very cumbersome to create dummy classes just so that they implement your interface. One possible way to get around this might be to have an overloaded method that accepts 1) EditableText 2) JLabel and 3) JTextComponent. This, however, is not completely foolproof. There may be other swing elements with getText/setText methods, and you could end up with a lot of overloaded methods.

  2. This is the only way I know of to "accept anything that has a getText/setText method." Reflection. I don't work with Reflection much, but I think something like this may work:

    void foo(Object o)
    {
            Class<?> c = o.getClass();
            for (Method m : c.getDeclaredMethods())
            {
                if (m.getName().equals("setText")||m.getName().equals("getText"))
                {
                //do Something
                }
            }
    }
    

That's the only surefire way I know of to detect ANY and ALL classes with those methods. Hope this helps!

Azar
  • 1,086
  • 12
  • 27
0

You don't need all that inheritance. There's a much simpler solution:

public void setText(JComponent c,
                    String text) {

    Objects.requireNonNull(c, "Component cannot be null");

    if (c instanceof JTextComponent) {
        ((JTextComponent) c).setText(text);
    } else if (c instanceof JLabel) {
        ((JLabel) c).setText(text);
    } else {
        throw new IllegalArgumentException(
            "Cannot set text of a " + c.getClass().getName());
    }
}

A more robust and typesafe approach is to write a couple overloaded methods:

public void setText(JTextComponent tc,
                    String text) {
    tc.setText(text);
}

public void setText(JLabel label,
                    String text) {
    label.setText(text);
}

This allows your other code to always call setText(component, text), as long as the type of each component is known at compile-time.

Inheritance and reflection can get the job done, but they're not the best way.

VGR
  • 40,506
  • 4
  • 48
  • 63