35

My current understanding of Inheritance implementation is that one should only extend a class if an IS-A relation is present. If the parent class can further have more specific child types with different functionality but will share common elements abstracted in the parent.

I'm questioning that understanding because of what my Java professor is recommending us to do. He has recommended that for a JSwing application we are building in class

One should extend all JSwing classes (JFrame,JButton,JTextBox,etc) into separate custom classes and specify GUI related customisation in them (like the component size, component label, etc)

So far so good, but he further goes on to advise that every JButton should have its own custom extended class even though the only distinguishing factor is their label.

For e.g. If the GUI has two buttons Okay and Cancel. He recommends they should be extended as below:

class OkayButton extends JButton{
    MainUI mui;
    public OkayButton(MainUI mui) {
        setSize(80,60);
        setText("Okay");
        this.mui = mui;
        mui.add(this);        
    }
}

class CancelButton extends JButton{
    MainUI mui;
    public CancelButton(MainUI mui) {
        setSize(80,60);
        setText("Cancel");
        this.mui = mui;
        mui.add(this);        
    }
}

As you can see the only difference is in the setText function.

So is this standard practice?

Btw, the course where this was discussed is called Best Programming Practices in Java

[Reply from the Prof]

So I discussed the problem with the professor and raised all the points mentioned in the answers.

His justification is that subclassing provides reusable code while following GUI design standards. For instance if the developer has used custom Okay and Cancel buttons in one Window, it will be easier to place the same buttons in other Windows as well.

I get the reason I suppose, but still it's just exploiting inheritance and making code fragile.

Later on, any developer could accidently call the setText on an Okay button and change it. The subclass just becomes nuisance in that case.

  • 3
    Why extend JButton and call public methods in its constructor, when you can simply create a JButton and call those same public methods outside of the class? – user22815 May 4 '16 at 15:44
  • 17
    stay away from that professor if you can. This is quite far from being best practices. Code duplication like you illustrated is a very bad smell. – njzk2 May 4 '16 at 16:22
  • 7
    Just ask him why, don't hesitate to communicate his motivation here. – Alex May 4 '16 at 16:50
  • 9
    I want to downvote because that's awful code, but I also want to upvote because it's great that you're questioning instead of blindly accepting what a bad professor says. – Nic Hartley May 4 '16 at 19:37
  • 1
    @Alex I've updated the question with the prof's justification – Paras May 5 '16 at 6:03
21

This violates the Liskov Substitution Principle because an OkayButton cannot be substituted in any place a Button is expected. For example, you can change the label of any button as you like. But doing that with an OkayButton violates its internal invariants.

This is a classic misuse of inheritance for code reuse. Use a helper method instead.

Another reason not to do this is that this is just a convoluted way of achieving the same thing that linear code would.

  • This doesn't violate LSP unless the OkayButton has the invariant you're thinking of. The OkayButton may claim not to have any extra invariants, it may merely consider the text as a default and intend to fully support modifications to the text. Still not a good idea, but not for that reason. – hvd May 5 '16 at 12:09
  • It doesn't violate it because you can. I.e., if a method activateButton(Button btn) expects a button, you can easily give it an instance of OkayButton. The principle does not mean that it has to have exactly the same functionality, as this would make inheritance pretty much useless. – Sebb May 5 '16 at 13:55
  • 1
    @Sebb but you can't use it with a function setText(button, "x") because that violates the (assumed) invariant. It's true that this particular OkayButton might not have any interesting invariant so I guess I picked a bad example. But it might. For example, what if the click handler says if (myText == "OK") ProcessOK(); else ProcessCancel();? Then the text is part of the invariant. – usr May 5 '16 at 13:57
  • @usr I didn't think about the text being part of the invariant. You're right, it's violated then. – Sebb May 5 '16 at 14:03
50

It's completely terrible in every possible way. At most, use a factory function to produce JButtons. You should only inherit from them if you have some serious extension needs.

  • 20
    +1. For more reference, look at Swing class hierarchy of AbstractButton. Then look the hierarchy of JToggleButton. Inheritance is used to conceptualise different types of button. But it's not used to differenciate business concepts (Yes,No, Continue, Cancel...). – Laiv May 4 '16 at 11:49
  • 2
    @Laiv: even having distinct types for, e.g. JButton, JCheckBox, JRadioButton, is just a concession to developers being familiar with other toolkits, like the plain AWT, where such types are the standard. In principle, they all could be handled by a single button class. It’s the combination of the model and UI delegate which makes the actual difference. – Holger May 4 '16 at 16:42
  • Yes they could be handled by a single button. However the actual hierarchy can be exposed as good practices. To create a new Button or just to delegate event's and behaivor's controll to another components is matter of preferences. If were me, I would extend Swing components in order to modelate my owm button and encapsulate its behaivors, actions,... Just to make easy its implementation in the project and make it easy for juniors. In web envs I prefer web components over too much event-handling. Anyways. You are right we can decouple UI from Behaivors. – Laiv May 4 '16 at 18:13
12

This is a very non-standard way of writing Swing code. Typically, you only rarely make subclasses of Swing UI components, most commonly JFrame (in order to set up child windows and event handlers), but even that subclassing is unnecessary and discouraged by many. Providing customization of text to buttons and so-on is usually performed by extending the AbstractAction class (or the Action interface it provides). This can provide text, icons, and other necessary visual customisation and link those to the actual code they represent. This is a far better way of writing UI code than the examples you show.

(by the way, google scholar has never heard of the paper you quote - do you have a more precise reference?)

  • What exactly is the "standard way"? I agree that writing a sub class for every component is probably a bad idea, but the official Swing tutorials still favor this practice. I reckon the professor just follows the style shown in the framework's official documentation. – COME FROM May 4 '16 at 9:19
  • @COMEFROM I'll admit to not having read the entire Swing tutorial, so maybe I've missed where this style is used, but the pages I've looked at tend to use the base classes and not drive subclasses, e.g. docs.oracle.com/javase/tutorial/uiswing/components/button.html – Jules May 4 '16 at 9:25
  • @Jules sorry about the confusion, I meant the course/paper the professor teaches is called Best Practices in Java – Paras May 4 '16 at 10:11
  • 1
    Generally true, but there is one other major exception - JPanel. It's actually more useful to subclass that than JFrame, since a panel is just an abstract container. – Ordous May 4 '16 at 12:21
10

IMHO, Best Programming Practices in Java are defined by Joshua Bloch's book, "Effective Java." It's great that your teacher is giving you OOP exercises and it's important to learn to read and write other people's styles of programming. But outside of Josh Bloch's book, opinions vary pretty widely about best practices.

If you are going to extend this class, you might as well take advantage of inheritance. Make a MyButton class to manage the common code and sub-class it for the variable parts:

class MyButton extends JButton{
    protected final MainUI mui;
    public MyButton(MainUI mui, String text) {
        setSize(80,60);
        setText(text);
        this.mui = mui;
        mui.add(this);        
    }
}

class OkayButton extends MyButton{
    public OkayButton(MainUI mui) {
        super(mui, "Okay");
    }
}

class CancelButton extends MyButton{
    public CancelButton(MainUI mui) {
        super(mui, "Cancel");
    }
}

When is this a good idea? When you use the types you've created! For instance, if you have a function to create a pop-up window and the signature is:

public void showPopUp(String text, JButton ok, JButton cancel)

Those types you just created aren't doing any good whatsoever. But:

public void showPopUp(String text, OkButton ok, CancelButton cancel)

Now you've created something useful.

  1. The compiler verifies that showPopUp takes an OkButton and a CancelButton. Someone reading the code knows how this function is meant to be used because this kind of documentation will cause a compile-time error if it goes out of date. This is a MAJOR benefit. The 1 or 2 empirical studies of the benefits of type safety found that human code comprehension was the only quantifiable benefit.

  2. This also prevents errors where you reverse the order of buttons you pass to the function. These errors are difficult to spot, but they are also pretty rare, so this is a MINOR benefit. This was used to sell type safety, but hasn't been empirically proven to be particularly useful.

  3. The first form of the function is more flexible because it will display any two buttons. Sometimes that's better than constraining what kind of buttons it will take. Just remember you have to test the any-button function under more circumstances - if it only works when you pass it exactly the right kind of buttons you aren't doing anyone any favors by pretending it will take any kind of button.

The problem with Object Oriented Programming is that it puts the cart before the horse. You need to write the function first to know what the signature is to know if it's worthwhile to make types for it. But in Java, you need to make your types first so that you can write the function signature.

For this reason, you might want to write some Clojure code to see how awesome it can be to write your functions first. You can code quickly, thinking about asymptotic complexity as much as possible, and Clojure's assumption of immutability prevents bugs as much as types do in Java.

I'm still a fan of static types for large projects and now use some functional utilities that allow me to write functions first and name my types later in Java. Just a thought.

P.S. I made the mui pointer final - it does not need to be mutable.

  • 6
    Of your 3 points, 1 and 3 can just as well be handled with generic JButtons and a proper input parameter naming scheme. point 2 is actually a downside in that it makes your code more tightly-coupled: what if you DO want the button order to be reversed? What if, instead of OK/Cancel buttons, you want to use Yes/No buttons? Will you make an entirely new showPopup method that takes a YesButton and a Nobutton? If you just use default JButtons, you don't need to make your types first because they already exist. – Nzall May 4 '16 at 12:41
  • Extending what @NateKerkhofs said, a modern IDE will show you the formal argument names while you are typing in the actual expressions. – Solomon Slow May 4 '16 at 17:19
8

I'd be willing to bet that your teacher doesn't actually believe that you should always extend a Swing component to use it. I bet they're just using this as an example to force you to practice extending classes. I wouldn't worry too much about real-world best practices just yet.

That being said, in the real world we favor composition over inheritance.

Your rule of "one should only extend a class if an IS-A relation is present" is incomplete. It should end with "...and we need to change the default behavior of the class" in big bold letters.

Your examples do not fit that criteria. You don't have to extend the JButton class just to set its text. You don't have to extend the JFrame class just to add components to it. You can do these things just fine using their default implementations, so adding inheritance is just adding unnecessary complications.

If I see a class that extends another class, I'm going to wonder what that class is changing. If you aren't changing anything, don't make me look through the class at all.

Back to your question: when should you extend a Java class? When you have a really, really, really good reason to extend a class.

Here's a specific example: one way to do custom painting (for a game or animation, or just for a custom component) is by extending the JPanel class. (More info on that here.) You extend the JPanel class because you need to override the paintComponent() function. You're actually changing the behavior of the class by doing this. You can't do custom painting with the default JPanel implementation.

But like I said, your teacher is probably just using these examples as an excuse to force you to practice extending classes.

  • 1
    Oh wait, you can set a Border that paints additional decorations. Or create a JLabel and pass a custom Icon implementation to it. It’s not necessary to subclass JPanel for painting (and why JPanel instead of JComponent). – Holger May 4 '16 at 16:46
  • 1
    I think you have the right idea here, but could probably pick better examples. – user22815 May 4 '16 at 16:49
  • 1
    But I do want to reiterate your answer is correct and you make good points. I just think the specific example and tutorial support it weakly. – user22815 May 4 '16 at 17:07
  • 1
    @KevinWorkman I don't know about Swing game development, but I have done some custom UIs in Swing and "not extending Swing classes so it is easy to wire up components and renderers via config" is fairly standard there. – user22815 May 4 '16 at 17:08
  • 1
    "I bet they're just using this as an example to force you to..." One of my major peeves! Instructors who teach how to do X using horribly deranged examples of why to do X. – Solomon Slow May 4 '16 at 17:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.