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.
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.
-
14So would I. But anyone who's ever written a widely reused base class (like a framework or media library) knows better than to expect application programmers to behave in a sane way. They'll subvert, misuse and distort your invention in ways you hadn't even thought were possible unless you lock it down with an iron grip. May 12, 2016 at 11:20
-
10@KilianFoth Ok, but honestly, how is that your problem what application programmers do?– coredumpMay 12, 2016 at 11:53
-
21@coredump People using my library badly create bad systems. Bad systems breed bad reputations. Users will not be able to distinguish Kilian's great code from Fred Random's monstrously unstable app. Result: I lose out in programming creds and customers. Making your code hard to misuse is a question of dollars and cents. May 12, 2016 at 11:57
-
23The statement "This element is not supposed to change, and if you want to change it, you haven't understood the existing design." is incredibly arrogant and not the attitude I would want as part of any library I worked with. If only I had a dime for every time some ridiculously over-encapsulated library left me with no mechanism to change some piece of inner state that needs to be changed because the author failed to anticipate an important use case... May 12, 2016 at 13:47
-
33@JimmyB Rule of thumb: If you think you know what your creation will be used for, you're wrong already. Bell conceived of the telephone as essentially a Muzak system. Kleenex was invented for the purpose of more conveniently removing makeup. Thomas Watson, president of IBM, once said "I think there is a world market for maybe five computers." Representative Jim Sensenbrenner, who introduced the PATRIOT Act in 2001, is on the record as saying that it was specifically intended to prevent the NSA from doing things they're doing "with PATRIOT act authority." And so on... May 12, 2016 at 15:08
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
extend
a class without careful thought. - always mark your classes as
final
except 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.
-
3You deserve at least +100 for mentioning the "fragile base class problem". :) May 12, 2016 at 11:40
-
7I'm not convinced by the points made here. Yes, the fragile base class is a problem, but final does not solve all the issues with changing an implementation. Your first example is bad because you are assuming you know all the possible use-cases for the PasswordChecker ("locking everyone out or allowing everyone access... is not OK" - says who?). Your last "therefore advisable..." list is really bad - you are basically advocating not extending anything and marking everything as final - which completely obliterates the usefulness of OOP, inheritance and code-reuse.– adelphusMay 12, 2016 at 17:00
-
5Your first example isn't an example of the fragile base class problem. In the fragile base class problem, changes to a base class break a subclass. But in that example, your subclass doesn't follow the contract of a subclass. These are two different problems. (Additionally, it actually reasonable that under certain circumstances, you might disable a password checker (say for development)) May 12, 2016 at 23:05
-
5"It avoids the fragile base class problem" - in the same way that killing yourself avoids being hungry. May 12, 2016 at 23:51
-
9@immibis, its more like avoiding eating to avoid getting food poisioning. Sure, never eating would be a problem. But eating only in those places that you trust makes a lot of sense. May 13, 2016 at 3:33
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.
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.
-
6Finally, compilers can, at least in theory => I've personally reviewed Clang's devirtualization pass, and I confirm it's used in practice. May 12, 2016 at 11:51
-
But can't the compiler tell in advance that nobody's overriding a class or method regardless of whether or not its marked final?– JesseTGMay 12, 2016 at 12:37
-
3@JesseTG If it has access to all the code at once, probably. What about separate file compilation, though? May 12, 2016 at 13:27
-
3@JesseTG devirtualization or (monomorphic/polymorphic) inline caching is a common technique in JIT compilers, since the system knows which classes are currently loaded, and can deoptimize the code if the assumption of no overriding methods turn out to be false. However, an ahead of time compiler cannot. When I compile one Java class and code that uses that class, I can later compile a subclass and pass an instance to the consuming code. The simplest way is to add another jar to the front of the classpath. The compiler can't know about all of that, since this happens at run time.– amonMay 12, 2016 at 13:31
-
5@MTilsted You can assume that strings are immutable as mutating strings via reflection can be prohibited via a
SecurityManager
. Most programs don't use it, but they also don't run any non-trusted code. +++ You have to assume that strings are immutable as otherwise you get zero security and a bunch of arbitrary bugs as bonus. Any programmer assuming Java strings can change in productive code is surely insane. May 13, 2016 at 1:09
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.
-
1
-
The JVM is smart enough to treat classes as final if they have no subclasses, even if they're not declared final. May 12, 2016 at 23:53
-
Downvoted because the 'performance' claim has been disproved so many times. May 16, 2016 at 14:51
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.
-
7-1 This answer describes everything that is wrong with software developers. If someone wants to reuse your class by subclassing, let them. Why would it be your responsibility how they use (or abuse) it? Basically, you're using final as a fk you, you're not using my class. *"Nobody paid me for making a class that could be subclassed". Are you serious? That's exactly why software engineers are employed - to create solid, reusable code.– adelphusMay 13, 2016 at 13:20
-
5-1 Just make everything private, so nobody will every even think of subclassing..– M4ksMay 13, 2016 at 16:32
-
3@adelphus While the wording of this answer is blunt, bordering on harsh, it isn't a "wrong" point of view. In fact it's the same point of view as the majority of answers on this question so far only with a less clinical tone. May 13, 2016 at 17:28
-
+1 for mentioning that you can remove 'final'. It is arrogant to claim foreknowledge of all possible uses of your code. Yet it is humble to make it clear that you cannot maintain some possible uses, and that those uses would require maintaining a fork.– gmathtMay 17, 2016 at 4:11
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.
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.
-
3If you targeting method is going to change why you ever made it public? And if you change the behaviour of a class significantly you need another version rather than overprotective
final
– M4ksMay 13, 2016 at 16:30 -
I have no idea why this would change. Hobbits are tricksie and the change was required. The point is that if I build it as a final I prevent inheritance which protects other people from having my changes infect their code. Nov 7, 2016 at 20:18
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).
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
final
? Many people (including me) find that it's a good design to make every non-abstract classfinal
.final
.