Java: When to create a final class
A final class is simply a class that can’t be extended.
This…final class Animal {
…
}
class Dog …prevents this.extends Animal {
…
}
It does not mean that all fields in the class are automatically final or that all references to this class would act as if they were declared as final.
When is this useful?
You should make a class final when the alternative—allowing subclassing—opens up for too many complications and becomes error prone.
Allowing subclassing may not seem like a big deal at first, but you should know that it’s notoriously difficult to get inheritance correct, and that it’s better to disallow it altogether than to get it wrong.
Here’s a non-exhaustive list of things to keep in mind when allowing classes to be extended:
-
Implementing
equalsbecomes trickySuppose an
Animalhas a single property:name. Should anAnimalwith name “Fido” equal aDogwith name “Fido”?@Override boolean equals(Object o) { return o instanceof Animal && this.name.equals(((Animal) o).name); }Seems reasonable, no?
equalshowever, must by contract, be reflexive: ifxequalsy, thenymust equalx. In other words, theDogobject must thenequaltheAnimalobject too. But what ifDoghas an additional propertybreedthat anAnimallacks? There’s no obvious solution here! Angelika Lagner and Klaus Kreft dedicate a whole article to this subject: Secrets of equals(). -
Seemingly innocent overrides can have surprising effects
If a private method class calls a public method in the base class, then overriding the public method may have unexpected side-effects on the inner workings of the base class.
-
Class invariants may break
The base class perhaps maintains certain internal invariants. Perhaps it’s immutable, or intended to be thread-safe. There’s no way to enforce such design decisions upon subclasses. If you receive an object of the base class type, you technically can’t assume it’s immutable or thread-safe unless it’s
final.
Put differently: Class inheritance is a flexible language feature, and a powerful footgun.
Concluding with a quote from Effective Java:
Item 19: Design and document for inheritance or else prohibit it
[…] designing a class for inheritance requires great effort and places substantial limitations on the class.
[…] The best solution to this problem is to prohibit subclassing in classes that are not designed and documented to be safely subclassed. Joshua Bloch