This is the second installment in the How to Make Your Java Code Not Suck series.
Fightin' (key)words
When debating Java code style, the one issue that has probably gotten me into the greatest number of heated arguments is the appropriate use of the keyword final
. I am not alone in my views on the subject, but it seems that there are great unwashed masses of developers who have not yet been converted to the Church of final
, and some of them seem to get quite annoyed when they see final
used in places where it is not absolutely required by the compiler.
However, before I go into detail on what my exact position is on the subject, and why nonbelievers are doomed to burn in Mutability Hell, let’s recap what, exactly, final
means when it appears in Java code.
Warning: overload detected
First of all, any Java programmer worth his or her salt knows that final
actually has two completely different meanings in Java, depending on where it is used.
The first, and probably most commonly used, meaning of final
is as a modifier on declarations variables, fields, and parameters. When applied in front of one of these, it effectively makes the identifier a constant for the duration of his scope. This means that once the identifier is initialized, the value it refers to cannot be changed. Its use is generally mandated when defining constants (especially in conjuction with the static
keyword), and prior to Java 8, its use was required by the compiler whenever an anonymous class creates a closure around a variable in the enclosing scope. For primitives, the use of final
effectively makes the variable a true constant (within its scope), but when applied to objects, it only makes the reference constant, but not the object itself, so care must be taken to not confuse the two concepts. Examples of this use of final
are shown below.
public class FinalExample { final String exampleField; public FinalExample(final String exampleParameter) { this.exampleField = exampleParameter; } public void writeToFile(final File anotherExampleParameter) throws IOException { try (final exampleVariable = new FileWriter(anotherExampleParameter)) { exampleVariable.write(exampleField); } } }
The second, less commonly used (and probably less well understood), meaning of final
is as a modifier on classes and methods to restrict inheritance and method overriding. Applying final
to a class prevents the creation of subclasses that extend it. Applying final
to a method prevents subclasses from overriding that method. Examples of this meaning are shown below.
public class BaseClass { public final void explicitlyFinalMethod() { System.out.println("foo"); } } public final class FinalClass extends BaseClass { public void implicitlyFinalMethod() { System.out.println("bar"); } }
I am primarily concerned with the first meaning of final
in this article, though I will touch on the second a bit as well.
final
thesis
Now that we have covered the meanings of final
, we come back to the subject of when it ought to be used.
After experimenting with different styles over the course of several years, I have settled upon the strongly held conviction that final
should be applied to all fields, parameters, and variables except for cases where the value definitely needs to change over time (and only after considering the alternatives). The sole exceptions to this rule are constants defined on interfaces, which are implicitly final
without needing to use the keyword, and parameters of abstract methods, where final
has no effect.
Preemptive strike
Before I go on to answer the question of "Why?", I will first preemptively counter the reasons I’ve heard for "Why not?".
The most common argument against that I’ve heard when I have argued in favor of liberal use of final
is that it "clutters up" the code with all those "extra" keywords. To that I have the following things to say:
-
It’s only an extra 5 characters (6 if you count the space). It’s not like there’s a worldwide letter shortage or something. There’s a little extra typing involved, but it’s not so much as to be cumbersome.
-
In an IDE or a text editor with Java syntax highlighting, those
final
keywords at the beginnings of lines will appear in a different color than the surrounding text, providing you with nice visual markers of where your variable declarations are. -
If you’re concerned about verbose source code, Java is not the language for you. Address all complaints to James Gosling.
-
Purely cosmetic concerns take a back seat to functional ones.
On a soapbox
Now that’s I’ve gotten that out of the way, it’s time to explain why you should be using final
wherever you can.
The first, and perhaps most obvious, or at least most often cited, reason for using final
extensively is that it enables the compiler to catch cases where you unintentionally change a value that you didn’t intend to, due a copy and paste error or because you simply mistyped or confused two identifiers. This "eliminating a class of errors" argument is certainly valid and important, however, it is frequently over-cited by proponents of final
and may be found unconvincing by those who would counter that such errors would likely be caught by unit tests anyway.
What’s more important is the knowledge that the compiler will catch such issues. When you declare a variable to be final
, or when you’re reading someone else’s code and see a final
variable, you can be confident that the identifier always points to the same thing for its entire lifetime. This means that if you see the identifier used in an expression somewhere, you need only go back to the line where it was initialized to find out its value. This saves you the trouble of needing to scrutinize every line of a method to determine whether its value may have changed at any point, and gives you the peace of mind of knowing that you didn’t overlook such a line. When final
is applied consistently, this helps you to debug problems by allowing you to more quickly narrow down the possible causes, and it helps developers who have not seen the code before (or recently) to more easily understand what it does.
A benefit of somewhat lesser importance but still valuable comes about when final
is always used whenever it can be used throughout a codebase. In such a project, any variable that is not marked final
is immediately noticeable as something special (i.e., intentionally mutable) and therefore warranting further scrutiny by anyone reading the code. If you’re used to seeing final
variables, fields, and parameters everywhere and spot one that isn’t final
, you are likely to immediately go looking for the lines of code where its value is being changed and try to determine why it was made mutable.
Loop the loop
An often overlooked detail of the "for-each" for
loop syntax introduced in Java 5 is the the element variable can (and should) be declared final
, as in the following example.
public void printStrings(final Collection<String> strings) { for (final String s : strings) { System.out.println(s); } }
Though final
makes the for
declaration a bit longer, allowing the possibility that the element variable may change mid-iteration is likely to cause chaos and confusion for anyone debugging your code. If you change the variable intentionally within the loop iteration, someone trying to maintain your code might just hunt you down and murder you for causing them such headaches.
A few words on parameters
For a long time I debated whether I should mark method and constructor parameters as final
, since doing so tends to make parameter lists considerably longer. However, I eventually came to the realization that (in Java, at least) there are no good reasons to ever modify a parameter. The usual reasons that developers modify parameters, e.g., to "adjust" values or apply defaults or overrides, just result in the same identifier being used to represent two fundamentally different concepts (i.e., the value that was passed as an argument, and the "adjusted" value). There is no runtime cost to declaring multiple identifiers, so there is no need to "recycle" identifiers within a scope by reusing them for different things, and in fact doing so just causes confusion for anyone reading the code, who is likely to assume that any appearance of a parameter refers to the argument that was passed in. If you need to make such an "adjustment", assign it to a new variable, so that it is clear to anyone reading the code that it’s not necessarily the same value as the argument.
Therefore, always mark all parameters as final
. If the parameter list gets long, put each parameter on its own line, like so:
public void someMethod( final int firstParameter, final String secondParameter, final double thirdParameter ) { // do stuff }
The one exception to this is the case of a parameter of an abstract
method, i.e., a method declared within an interfaces or abstract class without providing an implementation. Declaring such parameters to be final
has no practical effect (in much the same way that declaring an interface method to be public
has no effect, since all interface methods are public
by definition), so it’s okay to leave off the final
in this case.
In the field of fields
Proper use of final
is especially important when declaring fields on a class, since the field identifiers hang around for the lifetime of each class instance.
It’s been well established that public
fields should only be used to define constants, and therefore should be final
(and, to be idiomatic, also static
). The sole exception to this is the case of a field defined in an interface, where all fields are implicitly public
, static
, and final
without needing to be explicitly marked as such.
However, in any case where you have a field that will be initialized when the object is created and never changed afterwards, you should make the field final
. This is especially true when defining a class that represents some sort of "value" or bundle of values in order to make each instance immutable. The virtues and use cases of immutability warrant a post of their own, but for now suffice it to say that by making classes immutable, you enable instances to be freely passed around in an entirely thread-safe manner, and different parts of the code can hold references the same instance, with each part protected from unexpected changes being made by the other parts, and without the class itself needing to make potentially expensive defensive copies of any data it hands out.
In many cases, such as with "bean" classes used in dependency injection containers, even if the class is not fully immutable, each instance is fully initialized up front with its dependencies, which never change after initialization (though the dependency objects themselves may contain internal state that changes over time). Though it is not always feasible given the limitations of some dependency injection systems (such as JPA) and framework base classes, when possible it is generally helpful to perform all initialization in the constructor and to mark the fields storing references to dependencies as final
. This allows you to validate them all in one place without needing to resort to registering "post-initialization" hooks. It also ensures that the references are not unexpectedly modified after the startup phase, which in many cases can have bizarre and often catastrophic results.
Stuff and nonsense
Note that I haven’t listed performance as a motivating use of final
. In discussions of final
, some people erroneously claim that using final
enables the compiler to implement optimizations that it would not be able to perform on mutable values. This is simply not true. The compiler can apply such optimizations, but it doesn’t need you to tell it what values are immutable. The Java compiler can analyze code and figure out which variables are "effectively `final`" all on its own without your help.
The reason that I bring this up is that some people use the fact that the above claim is erroneous as evidence against using final
, on the grounds that it is unnecessary to achieving performance optimizations. The reasons for using final
have nothing to do with performance and have everything to do with improving the reliability and maintainability of your code.
That other thing I mentioned
As you may (unless your brain is broken) recall, the final
keyword has two different meanings in Java. We’ve already covered its use when applied to fields, variables, and parameters to control the mutability of those identifiers. Now it is time to discuss its use in controlling extensibility of classes when it is applied to classes and methods.
Unlike with fields, where using final
should be treated as the default practice, applying it to method and classes is a bit more of an dark art.
As a general rule, final
should be applied whenever the correctness of the code is predicated on certain (or all) methods of a class not being overridden. An obvious example of this is when defining an immutable class, in which case, allowing it to be extended would allow for the possibility that a derived class is mutable, compromising the immutability guarantees of the base class.
On the opposite side, in cases where you are implementating behaviors that you do intend to be overridden or extended, you clearly should not use final
.
Unfortunately, in between these two positions is a significant amount of grey area. In order to decide whether to use final
in these cases (and to what extent), you must weigh the likelihood that someone will need to extend or override the existing behavior against the documentary benefit of clearing marking what code was not intended to be extended. For the most part, this will need to be a judgment call based on your own past experience. However, keep in mind that if you are writing code that will not be used outside your own project, you will probably be able to change your mind about the "`final`-ness" of a particular class or method without causing too much trouble. If this is the cause, when in doubt, it’s probably better to make things final
until you run into a reason to change it to non-final
. On the other hand, if you are writing libraries that will be used outside your project, it’s probably safer to make things non-final
in case someone needs to extend a class in a way that you did not anticipate. However, still consider using final
in these cases if it is likely that overriding a method will cause non-overridden functionality to break, or if it causes code that uses the class to break.
final
thoughts
This brings us to the end of this particular rant. The most important point to draw from this is that you should always use final
for local variables, fields, and parameters unless you have a really, really (really) good reason not to do so in a particular case.
As I stated previously, I have come to this position after having evolved my coding style over several years, at least primarily using final
for local variables, then gradually also applying it to fields and eventually parameters. I have never come across a case where I later regreted having used final
, but I have come across many cases when reading (or debugging) other people’s code and sorely wished they they had used final
consistently. Indeed, immutable variables are the default (and sometimes only) case in many functional programming languages, and for good reason.
The more you can leverage the compiler to prevent unexpected things from happening in your code, the happier you’ll be as a developer in the long run.