Where is final used?
There are a lot of Java developers that don’t understand the keyword
final. The keyword
final is one of your greatest allies in writing bug free maintainable code. It makes reading code much more deterministic and step debugging a breeze. And it makes your code side effect free.
Side effects are on of the most common causes of bugs and instability in imperative programming, and one of the most difficult to discover and correct.
One reason most people don’t understand why
final is such a powerful keyword, is that it has two distinct meanings and most people only really understand the first.
Marking a class as final
The most popular understanding of why to use the keyword
final is in marking a
final. This makes sure that no one can sub-class the class that is marked
final. This is like putting a sub class lock on your code, it means that the original developer intends that this behavior should never change.
Marking a method as final
Another similar use to the
class usage, but much less known, is to mark a
final. This is a seen in much rarer cases than marking an entire
final. It locks the
method so that sub classes can’t
override the behavior of the
method and shows the intent of the original developer that this should never change.
Marking a variable reference as final
The second most popular understanding of why to use the keyword
final is to mark variables as
final, this one is also the most misunderstood. This does something completely different than its use as a
method modifier. It shows the intention of the original developer that the reference to an object or primitive should be immutable and trying to change it should result in an error.
final vs immutable
Some clarification is in order here, because there seems to be a lot of confusion about what is actually immutable when a variable reference is marked
It is the variable reference that is immutable, not the Object itself.
When someone refers to an Object as immutable, such as
String object is immutable
they mean the internal state of the
String object can never change, this is completely different than
X is marked
final and thus immutable.
Marking a variable
final doesn’t magically make the Object instance that it is refering to immutable.
Lets take the following example:
private final List people = new ArrayList();
What this states is that the reference
people can not be changed to point to a different instance of
It does not modify the behavior of the
ArrayList implemenation in any way. The instance of the
ArrayList can still be modifed.
Person objects can be added and removed which mutates the
ArrayList as it normally would.
final keyword does here is shows the intention of the original developer, that what the
people variable refers to should never change.
If you later executed these instructions your code would not compile.
this.people = new ArrayList();
When should I use the keyword final?
Best practices show that you should use the keyword
final as liberally as possible. The restrictions that
final puts on what it is modifying always makes code easier to understand the intentions of the original developer and makes understanding the logic easier and makes step debugging easier as well.
Since marking a
final is pretty explanatory and you can reason fairly easily why this is done and what its benefits are I am going to discuss why you should always strive to mark as many variable references
final as possible.
The first two rules are fairly easy to implement, and show the most benefit when used
Always mark arguments to constructors and methods as final
public class Person(final String firstName, final String lastName)
public int getNextDay(final Date date)
This is a very powerful tool to help create easy to understand, and maintainable code, it shows to everyone that the arguments to never change. If they never change, as you read through the code to understand it you can be guaranteed that the argument values are consistent everywhere they are reference, they always referent to the same values that were passed in.
Always mark local variable final
One of the hardest things to debug is some long method that re-assigns a value over and over again in many different decision making constructs. Nested
if/elseif/else statements and
try/catch/finally blocks that re-use and mutate a local variable make it very difficult to statically reason what the expected behavior of the code is. It makes the method one big ad-hoc state machine. And the only way to understand the behavior is to step debug through the code, or worse pepper it with
print statements all through it in an attempt to trace what the behavior is depending on the inputs.
It also makes it very easy for someone to come along behind you and break something un-intentionally because they have re-assigned something and broken your ad-hoc state machine.
Functional languages like Erlang have single assignment variables as a core part of their paradigm, and they tend to be more deterministic, easier to read and reason about what the intention of the logic is and less buggy because there is never a huge ad-hoc state machine.
Marking all instance variable references final
This one is harder to stick to consistently from a conceptual stand point. Mainly because it goes against everything most Java developers have ever know. But the rewards for learning to apply this practice are tremendous.
The first benefit is you get all the same benefits as marking arguments and local variables as
final. You get easier to read and understand the intention of the original developer, you get simpler debugging and no side effects.
The second benefit of marking all instance variables as
final is that you gain thread safety immediately, when all instance variables are
final it makes the instance of the class immutable. Immutable instances can be shared without any locking semantics among as many threads as you wish since there is never the chance of data changing, so that means no contention and no race conditions.
Dealing with immutable data might seem like a hassle, but as soon as you start working with the data structures concurrently, dealing with immutable data structures is simple compared to dealing with concurrency primitives, locks, semaphores, synchronized blocks, counters and all the other baggage and boilerplate overhead that comes with concurrency in Java. All that concurrency overhead is extra code to be tested, debugged and maintained.
There is a performance angle as well
In previous versions of the JVM marking any variable references, instance, local and arguments as
final allowed the compiler to do some advanced optimizations through caching because it could be sure that the contents of the variable reference never changed. This combined with immutable objects made a significant difference in concurrent code execution. In the case of
final String declarations the compiler can aggressively optimize those out to almost nothing, since
final String declarations are considered string literals if you concatenate string literals with
+ it can just convert them all to one single string literal.
Some of the performance aspects of the
final keyword from previous compilers are handled with the most current JIT, but using
final just for performance isn’t what this article is about, it is about increasing the quality of your code base, making your logic easier to discern the intention of the developer and making it more maintainable over the long run. Performance enhancements are just a bonus to these practices.
So the final word on final is
Try to apply the
final modifier everywhere you can to make as many variables as possible single assignment, the more you do it the more you will appreciate the entire class of bugs and logic complications it allows you to eliminate through the single assignment variable idiom.