TL;DR
Attempting to use composition first, before attempting to use inheritance, prevents naive mistakes. Also, Class Inheritance:
- Is easy to misuse.
- Is less versatile than composition.
- Is less versatile than interface inheritance.
- Requires more maintenance than composition.
- Provides some good guarantees about the derived types. Consider if it is worth the points above.
- Should be used when we need to follow the Liskov substitution principle.
- Can be necesary to extend poorly designed third party systems. Blame the third party.
Also, Java is not perfect.
Composition over Inheritance
Why does inheritance have so much hate in OOP and industry?
I think "hate" is not a fair representation. You will find lots of advice towards using composition over inheritance, because:
- Composition is much more versatile.
- Inheritance is easy to abuse, and beginners could avoid many naive mistakes by trying to use composition first.
Below are examples where a developer may want to solve the problem with inheritance, but it is not the best option.
Not exactly "is-a"
For example, sometimes people will use inheritance for things that fail the "is-a" relationship. Well, rather, I should say that fit much closer a "behaves-as" relationship. For example, in a video game I would have a lot objects that are visible, that I need to make sure to render them properly. It can be tempted to say that - for example - "Enemy is-a Visible", thus creating a Visible
class. However, that is wrong. The correct thing to do is to make an IVisible
interface.
In video games, sometimes you will find a push to say, "Enemy has-a Visible component". That has some advantages, for example, if I want to create an enemy that can turn invisible. Note: Yes, I could have a bool IsVisible
and check, but if I have a list of Visible
, and I remove it, I do not have to check at all.
A green car is not another type of car
A “Green Car is-a Car”, but it is impractical, ill-advised, and just plain wrong to create a GreenCar
class when we can have a color attribute. That way, we do not need to create a new class every time we need a new color. Also, with inheritance, how do you change color? That is composition over inheritance.
Addendum:
Perhaps, you have another class, let us say: Building
. And just like Car
, Building
also has a color. Should you create a ColoredThing
base class? No. You should not. Why? Well, what if both Car
and Building
also have an owner... should it be OwnedThing
then? Nope.
You see, if you abstract any common attributes, you will end up with a badly designed inheritance tree, and wanting to use multiple inheritance where it is not solving any real problem.
What if you need a list of things that have color? Should you use a ColoredThing
class then? No. You should use an interface.
Why? Again, what do you do when you also need a list of OwnedThing
? - It is about being open to extension. Interface is better because it does not narrow the possibilities to extend the system as much as a class does.
Is that what you call copy and paste? I would argue that is not copy and paste.
Ok, there is an argument for not repeating yourself there... One day you will discover that cars and buildings do not have a single color, they have multiple! Now you have to add more colors to all the things... I will grant that is bad, you should be able to change these things in a single place. No matter, changing the attribute of a type is easier than replacing a base class... Replace all your color attributes with a Coloring
class, and your Coloring
can have multiple colors.
From Multiple Inheritance to Component Systems
For example, if in the video game, the "Avatar is-a Soldier" (can use guns and shoot stuff). And in addition, the "Avatar is-a Pilot" (can drive vehicles), it can be tempting to use multiple inheritance.
Leaving aside that we have plenty of languages without multiple inheritance...
How do you model the Avatar
learning a new ability? Under this model, you could have bool flags. The Avatar
is created being a disabled Pilot. The more abilities you want to program in the game, the more stuff like that you need to add.
Multiple inheritance will become hard to maintain. Using interfaces will lead to code duplication, also hard to maintain. Use composition: the "Avatar has PilotAbility".
As you can see, composition is in particular more versatile than inheritance in languages without multiple class inheritance. Futhermore, I would argue that many languages do not have it by design, not by oversight. Multiple inheritance is easy to misuse, so it is restricted to the sane cases (interfaces), and we have alternative for other situations.
When to use Inheritance
Is there a valid case to use inheritance noadays?
We have - at least - three elephants in the room. I will quickly dispose of two of them and acknowledge the third...
You do not need inheritance. Evidence is that some languages just straight up do not have it, and they are perfectly usable. However, inheritance can be convenient.
Sometimes, you need to work with a system that require inheritance for extension. In that case you need inheritance, because the design of that system has narrowed your options. However, it does not imply that such system (or an alternative one that serves the same purpose) was impossible without inheritance. Why did they choose inheritance? Convenience. Note: I will shove down creating custom exceptions here.
Interface inheritance is inheritance. However, I will be dealing with interface inheritance as if it were something else. Note: the advent of Default Methods removes some use cases of class inheritance.
Note: inheritance sometimes is an optimization, I will not be talking about that, I think optimization as an argument for inheritance is too weak. However, I want to mention that UI and widget libraries often take advantage of the fact that calling a virtual method can perform better than callbacks, listeners, observers, continuations, et. al. In fact, if your language does not support function references (Java, for example), using inheritance and calling virtual methods will be the most efficient way to delegate and notify.
Thus, we will not be talking about the need of inheritance, Instead we will be talking about the convenience.
With that said, yes, there are valid use cases.
Liskov substitution principle
You should be able to use the derived type everywhere you can use the base type.
Sometimes people would resort to inheritance, and then have to hide, set to null, or throw exceptions in members they do not want. In that situation is better to use composition. Unless we are extending a third party system, and inheritance was the only option (because we need virtual methods or we need to call methods that expect the base class), blame the third party.
I did a search in my code base, discarded all the cases where I had to use inheritance to extend a third party system, discarded all the cases that I would do differently with default methods, and arrived to the following example:
Consider a library that has a SortedSet
type and that has a method which allows creating a SubSet
from it. These subsets are not like the base type (they do not actually store the items), instead they are a view defined by a range and a reference to the set of which they are a subset. However, a subset "is-a" set.
In this case, I will have another type that actually stores the items, and SortedSet
will have a reference to it (composition). Now both the SortedSet
and the SubSet
would have a reference to the same storage object. The SubSet
type also has other two members that define the range. There are also virtual methods in SortedSet
that SubSet
will override (for example to allow you to take subsets of the subsets).
What is important here is that the SubSet
from the point of view of the consumer is a SortedSet
. In fact, I can make my SubSet
class private and only expose it from SortedSet
, as a SortedSet
, without any loss of functionality (and I have done so).
Now, why was it important to use inheritance instead of composition? Because the derived class should be usable everywhere the base case class should be useable (Liskov substitution principle).
Why did I not use interfaces? Because the implementation of the subset is very tight to its parent, they share a lot of code, and are aware of each other (in fact the parent instantiates the derived class). Furthermore, you do not need to know they are different classes, at all, to be able to use them. While if I had used an interface, you had to remember to define your parameters as the interface instead of the class to be able to allow both.
Note: The SortedSet
also implements an ISet
interface. Moreover, yes, they are all generic.
Inheritance as guarantees
Inheritance, when combined with proper use of virtual and sealed(.NET)/final(Java) members, means that you have a tight control over the implementation, and thus you can trust it.
You will find this useful in scenarios where you deal with external resources, which you would need to pass around to allow extension. If the derived class can use the external resource in ways that would screw up the system, then we do not want everybody using derived classes that mess with the system.
In these situations, you would create an inheritance tree with the derived classes you might need. However, if designed properly, the consumer of the library can use them in composition, without the need of inheritance.
Bad composition
When is it considered a bad design pattern to use composition over inheritance?
Breaking the Liskov substitution principle
When you intend the types are replacements. By wrapping a type instead of inheriting from it, I would not be able to use my new type in all the places where I can use the base type. Thus, if I intend my type as a replacement, I should be using inheritance.
Notice that inheritance and composition should work together. For instance, thanks to composition, I can remove or change the components of an object, and thanks to inheritance, I can use replacements of different types.
Java
Java source code is full of inheritance and deep inheritance. Does it mean Java source is poorly designed?
I read this as an Argument from authority in disguise: If Java uses inheritance, and the people that created Java are smart, it must be smart to use inheritance.
Well, hold your horses...
First, let us be clear on that Java is - at least - three things: the language, the standard library and the virtual machine. I know you ask about the standard library. Soon it will make sense why I point this out.
Now, Java is not perfect. Some aspect of its design show its age. Sure, we have deprecations, but some things are too deeply rooted in Java that deprecation is not feasible. I will come back to the archaic parts of Java. First, let us see what parts use inheritance appropriately.
Appropriate use of inheritance
Disclaimer: I do not know the standard library by memory, or ever did. In fact, it has been a while since I used Java for anything serious. Thus, I am looking at this: Hierarchy For All Packages.
Using inheritance for exceptions (Throwable
) and enums, is the state of the art.
There are things that interface with external resources, such as UI and database related types. I will give these a pass.
I see some threading related types use inheritance. This makes sense, just as with external resources, inheritance provides useful guarantees. We can make a similar argument for reflection.
Archaic but justified
The abstract data structures probably should be interfaces (and perhaps accompanied by utility classes). I am aware that these abstract data structures have usable common code, and thus simply making those interfaces will not do. In addition, default methods did not exist back when those were created (default methods required improvements to the language - and probably the virtual machine), and Java does not have something like extension methods. Therefore, they are archaic but justified.
I would argue that the whole listener model is a bad, at least for modern standards. The thing is that Java lacks a way to reference functions (virtual machine limitation). Therefore, they are stuck with this. Before Java had anonymous classes (language improvement), you would rather implement EventListener
in your class. That was preferable to creating a bunch of small classes to implement each event type and solving what members you need to share and how to do it without breaking encapsulation. In the implementation of EventListener
you would need to sort out the type of events, requiring to match the type of the EventObject
. Thus, we need many sub types of EventObject
. Therefore, this is archaic but justified.
The lang visitors, the text formats and other stuff that require some form of iteration, or work on strings or binary streams relies on inheritance. Considering that listeners are a pain (and there were nothing like the Stream API) it was better to use inheritance. Thus, these are all in the same boat: archaic but justified.
Other notes
Copy and Paste
Copy and paste is the second oldest form of reutilization (being transcription the first one). With that said, we would rarely, (in fact, I would argue, we would never) advocate for copy and paste.
In the example:
Class Shape {
private String name;
private double area;
public double getArea()
}
Class Rectangle extends Shape {
}
It is not clear why you need a Rectangle
class. I would argue that you could work with Shape
only since Rectangle
is not adding anything. If Rectangle
will be adding more stuff, then inheritance would be useful here.
they would rather copy and paste the class Shape had only them 2 fields and that single method
No, please, do not copy and paste just to avoid inheritance. The argument is between inheritance and composition: Should you say that the "Rectangle has a Shape"? Refer to the Liskov substitution principle, if Rectangle
is meant to be used where Shape
is used, use inheritance.
On method overloads
If I create a method that accepts type T, it will also accept any object of a type derived from T. If you cannot modify this class, and you need this method, you will have to inherit from T. Now, the suggestion is to be as permissive as possible with what you accept, and usually means using an interface that closely resembles how the method will be using the object.
If we are working in a dynamically typed language, there is no method overloading and we will have to interrogate the objects, but that is another thing.
See Liskov substitution principle and Interface segregation principle.