Swing: Composition (Has-A) vs Inheritance (Is-A)
Even a beginning object orientated developer probably knows that when possible you should favor Composition over Inheritance. Some people may also know this as the difference between Has-A (Composition) and Is-A (Inheritance). Annoyingly, for whatever reason, developers don’t seem to apply this principle when writing Java Swing code. Before we cover that let’s have a little composition vs. inheritance primer or recap whatever the case may be for your level of experience.
Composition vs. Inheritance Primer/Recap
It is generally better not to sub-class and instead have a class that has other classes as properties and offers methods to control the behavior of those owned classes. That is probably a poor description so to illustrate the concept let’s consider an example. Let’s say you need to implement a caching class for some data that is expensive performance-wise to retrieve. There are two ways to accomplish this. You could extend a collection like HashMap and then your cache is a HashMap with some custom methods to manipulate the cache. The other way would be to create a class that has a instance of HashMap as a property and then offers methods to manipulate that HashMap. From the description it may sound like these are very similar. However, the implementation and consequences are vastly different. I will let the following examples illustrate.
Example of Composition (Has-A)
Example of Inheritance (Is-A)
I don’t think you will find many people that could argue with a straight face that implementing this with inheritance is the preferred way. In this example using composition is clearly the better design.
Apply Composition Over Inheritance to Java Swing Code
There is no reason to not apply the same preference for composition to Swing code. Yet, in my experience it is much more common (practically universal) for Swing code to be implemented with inheritance. I believe this is because all of the Swing example code in the Swing Tutorial shows inheritance rather than composition. However, tutorial code is meant to be a quick example of the demonstrated component and not production quality code. Here is an example of what you most certainly will see if you develop with Swing:
If you are doing it this way you are doing it wrong. There is no flexibility here. Someone that wants to reuse your ButtonPanel is stuck using it the way you lay it out. What if another developer wants all the functionality of the ButtonPanel but they want the buttons to be stacked vertically but your layout lays them out horizontally? It would make much more sense for ButtonPanel to not be a JPanel and instead have a JPanel with getters for the JPanel and the JButtons. This would let someone reuse ButtonPanel much more easily.
I have a full example here. This example consists of two classes and is a fully functioning Swing application. Note, that I do not inherit JPanel or JFrame instead I have classes that have JPanels and JFrames. The advantage here is that UrlEntryPanel can easily be reused. It offers a default layout method which returns a panel. However, anyone using this class is free to ignore that method and instead use the getters for the buttons and put them in a container of their choice however they need them laid out. Yet they still get all of the functionality of the UrlEntryPanel.
When is it OK to sub-class a Swing Component?
Does this mean you should never extend a Swing component? Nope. You simply need to know when it is appropriate to extend a Swing component. My rule-of-thumb is if you need to override the paintComponent() method of a Component then you need to use inheritance. Otherwise, use composition and make a class that has your needed components. If you aren’t over-riding paintComponent() then you really don’t have a new type of a component and inheritance isn’t appropriate.