It seems Java has had the power to declare classes not-derivable for ages, and now C++ has it too. However, in the light of the Open/Close principle in SOLID, why would that be useful? To me, the final keyword sounds just like friend - it is legal, but if you are using it, most probably the design is wrong. Please provide some examples where a non-derivable class would be a part of a great architecture or design pattern.
- 178
- 7,169
10 Answers
final expresses intent. It tells the user of a class, method or variable "This element is not supposed to change, and if you want to change it, you haven't understood the existing design."
This is important because program architecture would be really, really hard if you had to anticipate that every class and every method you ever write might be changed to do something completely different by a subclass. It is much better to decide up-front which elements are supposed to be changeable and which aren't, and to enforce the unchangeablility via final.
You could also do this via comments and architecture documents, but it is always better to let the compiler enforce things that it can than to hope that future users will read and obey the documentation.
- 110,899
It avoids the Fragile Base Class Problem. Every class comes with a set of implicit or explicit guarantees and invariants. The Liskov Substitution Principle mandates that all subtypes of that class must also provide all these guarantees. However, it is really easy to violate this if we don't use final. For example, let's have a password checker:
public class PasswordChecker {
public boolean passwordIsOk(String password) {
return password == "s3cret";
}
}
If we allow that class to be overridden, one implementation could lock out everyone, another might give everyone access:
public class OpenDoor extends PasswordChecker {
public boolean passwordIsOk(String password) {
return true;
}
}
This is usually not OK, since the subclasses now have behaviour that is very incompatible to the original. If we really intend the class to be extended with other behaviour, a Chain of Responsibility would be better:
PasswordChecker passwordChecker =
new DefaultPasswordChecker(null);
// or:
PasswordChecker passwordChecker =
new OpenDoor(null);
// or:
PasswordChecker passwordChecker =
new DefaultPasswordChecker(
new OpenDoor(null)
);
public interface PasswordChecker {
boolean passwordIsOk(String password);
}
public final class DefaultPasswordChecker implements PasswordChecker {
private PasswordChecker next;
public DefaultPasswordChecker(PasswordChecker next) {
this.next = next;
}
@Override
public boolean passwordIsOk(String password) {
if ("s3cret".equals(password)) return true;
if (next != null) return next.passwordIsOk(password);
return false;
}
}
public final class OpenDoor implements PasswordChecker {
private PasswordChecker next;
public OpenDoor(PasswordChecker next) {
this.next = next;
}
@Override
public boolean passwordIsOk(String password) {
return true;
}
}
The problem becomes more apparent when more a complicated class calls its own methods, and those methods can be overridden. I sometimes encounter this when pretty-printing a data structure or writing HTML. Each method is responsible for some widget.
public class Page {
...;
@Override
public String toString() {
PrintWriter out = ...;
out.print("<!DOCTYPE html>");
out.print("<html>");
out.print("<head>");
out.print("</head>");
out.print("<body>");
writeHeader(out);
writeMainContent(out);
writeMainFooter(out);
out.print("</body>");
out.print("</html>");
...
}
void writeMainContent(PrintWriter out) {
out.print("<div class='article'>");
out.print(htmlEscapedContent);
out.print("</div>");
}
...
}
I now create a subclass that adds a bit more styling:
class SpiffyPage extends Page {
...;
@Override
void writeMainContent(PrintWriter out) {
out.print("<div class='row'>");
out.print("<div class='col-md-8'>");
super.writeMainContent(out);
out.print("</div>");
out.print("<div class='col-md-4'>");
out.print("<h4>About the Author</h4>");
out.print(htmlEscapedAuthorInfo);
out.print("</div>");
out.print("</div>");
}
}
Now ignoring for a moment that this is not a very good way to generate HTML pages, what happens if I want to change the layout yet again? I'd have to create a SpiffyPage subclass that somehow wraps that content. What we can see here is an accidental application of the template method pattern. Template methods are well-defined extension points in a base class that are intended to be overridden.
And what happens if the base class changes? If the HTML contents change too much, this could break the layout provided by the subclasses. It is therefore not really safe to change the base class afterwards. This is not apparent if all your classes are in the same project, but very noticeable if the base class is part of some published software that other people build upon.
If this extension strategy was intended, we could have allowed the user to swap out the way how each part is generated. Either, there could be a Strategy for each block that can be provided externally. Or, we could nest Decorators. This would be equivalent to the above code, but far more explicit and far more flexible:
Page page = ...;
page.decorateLayout(current -> new SpiffyPageDecorator(current));
print(page.toString());
public interface PageLayout {
void writePage(PrintWriter out, PageLayout top);
void writeMainContent(PrintWriter out, PageLayout top);
...
}
public final class Page {
private PageLayout layout = new DefaultPageLayout();
public void decorateLayout(Function<PageLayout, PageLayout> wrapper) {
layout = wrapper.apply(layout);
}
...
@Override public String toString() {
PrintWriter out = ...;
layout.writePage(out, layout);
...
}
}
public final class DefaultPageLayout implements PageLayout {
@Override public void writeLayout(PrintWriter out, PageLayout top) {
out.print("<!DOCTYPE html>");
out.print("<html>");
out.print("<head>");
out.print("</head>");
out.print("<body>");
top.writeHeader(out, top);
top.writeMainContent(out, top);
top.writeMainFooter(out, top);
out.print("</body>");
out.print("</html>");
}
@Override public void writeMainContent(PrintWriter out, PageLayout top) {
... /* as above*/
}
}
public final class SpiffyPageDecorator implements PageLayout {
private PageLayout inner;
public SpiffyPageDecorator(PageLayout inner) {
this.inner = inner;
}
@Override
void writePage(PrintWriter out, PageLayout top) {
inner.writePage(out, top);
}
@Override
void writeMainContent(PrintWriter out, PageLayout top) {
...
inner.writeMainContent(out, top);
...
}
}
(The additional top parameter is necessary to make sure that the calls to writeMainContent go through the top of the decorator chain. This emulates a feature of subclassing called open recursion.)
If we have multiple decorators, we can now mix them more freely.
Far more often than the desire to slightly adapt existing functionality is the desire to reuse some part of an existing class. I have seen a case where someone wanted a class where you could add items and iterate over all of them. The correct solution would have been to:
final class Thingies implements Iterable<Thing> {
private ArrayList<Thing> thingList = new ArrayList<>();
@Override public Iterator<Thing> iterator() {
return thingList.iterator();
}
public void add(Thing thing) {
thingList.add(thing);
}
... // custom methods
}
Instead, they created a subclass:
class Thingies extends ArrayList<Thing> {
... // custom methods
}
This suddenly means that the whole interface of ArrayList has become part of our interface. Users can remove() things, or get() things at specific indices. This was intended that way? OK. But often, we don't carefully think through all consequences.
It is therefore advisable to
- never
extenda class without careful thought. - always mark your classes as
finalexcept if you intend for any method to be overridden. - create interfaces where you want to swap out an implementation, e.g. for unit testing.
There are many examples where this “rule” has to be broken, but it usually guides you to a good, flexible design, and avoids bugs due to unintended changes in base classes (or unintended uses of the subclass as an instance of the base class).
Some languages have stricter enforcement mechanisms:
- All methods are final by default and have to be marked explicitly as
virtual - They provide private inheritance that doesn't inherit the interface but only the implementation.
- They require base class methods to be marked as virtual, and require all overrides to be marked as well. This avoids problems where a subclass defined a new method, but a method with the same signature was later added to the base class but not intended as virtual.
- 135,795
I'm surprised that no one has yet mentioned Effective Java, 2nd Edition by Joshua Bloch (which should be required reading for every Java developer at least). Item 17 in the book discusses this in detail, and is titled: "Design and document for inheritance or else prohibit it".
I won't repeat all the good advice in the book, but these particular paragraphs seem relevant:
But what about ordinary concrete classes? Traditionally, they are neither final nor designed and documented for subclassing, but this state of affairs is dangerous. Each time a change is made in such a class, there is a chance that client classes that extend the class will break. This is not just a theoretical problem. It is not uncommon to receive subclassing-related bug reports after modifying the internals of a nonfinal concrete class that was not designed and documented for inheritance.
The best solution to this problem is to prohibit subclassing in classes that are not designed and documented to be safely subclassed. There are two ways to prohibit subclassing. The easier of the two is to declare the class final. The alternative is to make all the constructors private or package-private and to add public static factories in place of the constructors. This alternative, which provides the flexibility to use subclasses internally, is discussed in Item 15. Either approach is acceptable.
- 3,328
One of the reasons final is useful is that it makes sure you cannot subclass a class in a way which would violate the parent class's contract. Such subclassing would be a violation of SOLID (most of all "L") and making a class final prevents it.
One typical example is making it impossible to subclass an immutable class in a way which would make the subclass mutable. In certain cases such a change of behavior could lead to very surprising effects, for example when you use something as keys in a map thinking the key is immutable while in reality you are using a subclass which is mutable.
In Java, a lot of interesting security issues could be introduced if you were able to subclass String and make it mutable (or made it call back home when someone calls its methods, thus possibly pulling sensitive data out of the system) as these objects are passed around some internal code related to class loading and security.
Final is also sometimes helpful in preventing simple mistakes such as re-using the same variable for two things within a method, etc. In Scala, you are encouraged to use only val which roughly corresponds to final variables in Java, and actually any use of a var or non-final variable is looked at with suspicion.
Finally, compilers can, at least in theory, perform some extra optimizations when they know that a class or method is final, since when you call a method on a final class you know exactly which method will be called and don't have to go through virtual method table to check inheritance.
- 10,400
- 3,584
The second reason is performance . The first reason is because some classes have important behaviors or states that are not supposed to be changed in order to allow the system to work. For example if i have a class "PasswordCheck" and to build that class i've hired a team of security experts and this class communicates with hundreds of ATMs with well studied and defined procols. Allow a new hired guy fresh out of university make a "TrustMePasswordCheck" class that extends the above class could be very harmful for my system; those methods are not supposed to be overridden, that's it.
- 678
When I need a class, I'll write a class. If I don't need subclasses, I don't care about subclasses. I make sure that my class behaves as intended, and the places where I use the class assume that the class behaves as intended.
If anyone wants to subclass my class, I want to fully deny any responsibility for what happens. I achieve that by making the class "final". If you want to subclass it, remember that I didn't take subclassing into account while I wrote the class. So you have to take the class source code, remove the "final", and from then on anything that happens is fully your responsibility.
You think that's "not object oriented"? I was paid to make a class that does what it's supposed to do. Nobody paid me for making a class that could be subclassed. If you get paid to make my class reusable, you are welcome to do it. Start by removing the "final" keyword.
(Other than that, "final" often allows substantial optimisations. For example, in Swift "final" on a public class, or on a method of a public class, means that the compiler can fully know what code a method call will execute, and can replace dynamic dispatch with static dispatch (tiny benefit) and often replace static dispatch with inlining (possibly huge benefit)).
adelphus: What is so hard to understand about "if you want to subclass it, take the source code, remove the 'final', and it's your responsibility"? "final" equals "fair warning".
And I'm not paid to make reusable code. I am paid to write code that does what it's supposed to do. If I'm paid to make two similar bits of code, I extract the common parts because that's cheaper and I'm not paid to waste my time. Making code reusable that isn't reused is a waste of my time.
M4ks: You always make everything private that isn't supposed to be accessed from the outside. Again, if you want to subclass, you take the source code, change things to "protected" if you need, and take responsibility for what you do. If you think you need to access things that I marked private, you better know what you are doing.
Both: Subclassing is a tiny, tiny portion of reusing code. Creating building blocks that can be adapted without subclassing is much more powerful and hugely benefits from "final" because the users of the blocks can rely on what they get.
- 49,096
Let's imagine that the SDK for a platform ships the following class:
class HTTPRequest {
void get(String url, String method = "GET");
void post(String url) {
get(url, "POST");
}
}
An application subclasses this class:
class MyHTTPRequest extends HTTPRequest {
void get(String url, String method = "GET") {
requestCounter++;
super.get(url, method);
}
}
All is fine and well, but someone working on the SDK decides that passing a method to get is silly, and makes the interface better making sure to enforce backwards compatibility.
class HTTPRequest {
@Deprecated
void get(String url, String method) {
request(url, method);
}
void get(String url) {
request(url, "GET");
}
void post(String url) {
request(url, "POST");
}
void request(String url, String method);
}
Everything seems fine, until the application from above is recompiled with the new SDK. Suddenly, the overriden get method isn't being called anymore, and the request aren't being counted.
This is called the fragile base class problem, because a seemingly innocous change results in a subclass breaking. Anytime change to which methods are called inside the class might cause a subclass to break. That tends mean that almost any change might cause a subclass to break.
Final prevents anybody from subclassing your class. That way, which methods inside the class can be changed without worrying that somewhere someone depends on exactly which method calls are made.
- 25,052
Final effectively means that your class is safe to change in the future without impacting any downstream inheritance based classes (because there are none), or any issues around thread safety of the class (I think there are cases where the final keyword on a field prevents some thread based high-jinx).
Final means that you are free to change how your class works without any unintended changes in behavior creeping into other people's code that relies on yours as a base.
As an example, I write a class called HobbitKiller, which is great, because all hobbits are tricksie and should probably die. Scratch that, they all definitely need to die.
You use this as a base class and add in an awesome new method to use a flamethrower, but use my class as a base because I have a great method for targeting the hobbits (in addition to being tricksie, they're quick), which you use to help aim your flamethrower.
Three months later I change the implementation of my targeting method. Now, at some future point when your upgrade your library, unbeknownst to you, your class's actual runtime implementation has fundamentally changed because of a change in the super class method you depend on (and generally do not control).
So for me to be a conscientious developer, and ensure smooth hobbit death into to the future using my class, I have to be very, very careful with any changes that I make to any classes that can be extended.
By removing the ability to extend except in cases where I am specifically intending to have the class extended, I save myself (and hopefully others) a lot of headaches.
- 19
To me it's a matter of design.
Let's suppose I have a program that calculates salaries for employees. If I have a class that returns number of working days between 2 dates based on the country (one class for each country), I will put that final, and provide a method for every enterprise to provide a free day only for their calendars.
Why? Simple. Let's say a developer wants to inherit base class WorkingDaysUSA in a class WorkingDaysUSAmyCompany and modify it to reflect that his enterprise will be closed for strike/maintenance/whatever reason the 2nd of mars.
The calculations for clients orders and delivery will reflect the delay and work accordingly when in runtime they call WorkingDaysUSAmyCompany.getWorkingDays(), but what happens when I calculate vacations time? Should I add the 2nd of mars as a holiday for everyone? No. But since the programmer used inheritance and I didn't protect the class this can lead to a confusion.
Or let's say they inherit and modify the class to reflect that this company doesn't work Saturdays where in the country they work half time on Saturday. Then a earthquake, electricity crisis or some circumstance makes the president declare 3 non-working days like it happened recently on Venezuela. If the method of the inherited class already subtracted each Saturday, my modifications on the original class could lead to subtract the same day twice. I would have to go to each subclass on each client and verify all changes are compatible.
Solution? Make the class final and provide a addFreeDay(companyID mycompany, Date freeDay) method. That way you are sure that when you call a WorkingDaysCountry class it's your main class and not a subclass
- 149
The use of final is not in any way a violation of SOLID principles. It is, unfortunately, extremely common to interpret the Open/Closed Principle ("software entities should be open for extension but closed for modification") as meaning "rather than modify a class, subclass it and add new features". This isn't what was originally meant by it, and is generally held not to be the best approach to achieving its goals.
The best way of complying with OCP is to design extension points into a class, by specifically providing abstract behaviours that are parameterised by injecting a dependency to the object (e.g. using the Strategy design pattern). These behaviours should be designed to use an interface so that new implementations do not rely on inheritance.
Another approach is to implement your class with its public API as an abstract class (or interface). You can then produce an entirely new implementation which can plug in to the same clients. If your new interface requires broadly similar behaviour as the original, you can either:
- use the Decorator design pattern to reuse the existing behaviour of the original, or
- refactor the parts of the behaviour that you want to keep into a helper object and use the same helper in your new implementation (refactoring isn't modification).
- 17,880
- 2
- 38
- 65