2005-10-26
The use of final in Java
Recently I while reviewing some Java code, I took issue with the use of the final modifier on local variables (though on fields it is fine). Here is why.
final local variables did not exist in the original Java language, as described in the JLS first edition. They were added in JDK1.1 to support inner classes (also see this email from Guy Steele). In a method without inner classes, they are never truly necessary: You can take the finals out, and the code will still compile and work.
Of course, final is is not truly necessary on fields either, so why do I advocate its use with fields but not with local variables? There are two technical reasons that favour the use of final with fields but not local variables, and then there is a general principle of programming.
First, note that Java has no pointers to local variables. This means that any code that can assign to a variable is lexically within the block where that variable is declared. So if you want to know if a variable is assigned to or not, simply seach the block for assignments. Assignments outside a block are simply irrelevant. If the block is so long that searching for assignments is inconvenient, that's a strong sign that the method is too long and should be decomposed.
The situation is quite different in C/C++. In those languages, you can take the address of a local variable, and pass it to a function which might hold onto that address somewhere. So for any variable that has its address taken, it becomes vey difficult to find relevant assignments (and references in C++ make this even worse). In those languages, being able to declare a variable as const, and having the compiler check that this assertion is honoured, is very useful.
Returning to fields in Java: Object references are effectively pointers, and a field of an object can potentially be assigned wherever a reference to that object is held. Of course, the use of access modifiers in Java tends to greatly reduce the places where a fields can actually be accessed. Most fields are private, and so can only be accessed in the delcaring class. But even for a private field, it remains more difficult to search a full class for assignments to a field than to search a method for assignments to a local variable. Classes grow much larger than methods, due to obstacles to decomposing them, and the fact that they simply represent a larger unit of abstraction. So final is more useful to exclude assignments to fields.
The second technical issue concerns synchronization. If a class is intended to be thread-safe, then you need to carefully consider the locking protocol, and ensure that it covers the fields of the object. But immutable fields do not require locking, and if all the fields of a class are immutable, then the class might be thread-safe without any locking. So for thread-safe classes, final can have meaning related to the locking requirements of a field. This does not make the difficulties of correct synchronization go away, but it does help in some cases.
In contrast, local variables are always thread-local (since in Java you cannot obtain pointers to them which might be passed to other threads), so no synchronization issues arise, and this implication of final is redundant.
Now, to the principle of programming. As I mentioned earlier, final is never truly necessary. So you could decide to never use it. Another option would be to use it whenever it can be applied. Another option is not to follow a strict rule, but to decide when to use final or not on a case-by-case basis, according to taste.
I propose the principle that you should follow fixed rules in programming wherever possible. You can find many examples of widely used rules that programmers adopt, where it would be possible to operate according to taste:
- Naming conventions.
- Indentation style.
- Brace style.
- Use of spaces inside line and blank lines.
- When to break long lines.
- Method length.
- Use of access modifiers.
Not all of these rules get written down, but if you look at the code of good programmers, or the core code of successful open source projects, they often follow certain rules on these matters. The rules differ between programmers and between projects, but rules still exist.
Why follow such rules? The advantage when you are writing code is simple: If you follow rules for such things, then the rules quickly become automatic habits. Then you do not need to think about these things, so you can think about other things instead: The logic of the code, good names for variables and methods, where to put comments, how the design is working out, etc. You are able to focus your mental efforts on the areas that cannot be reduced to rules.
There is a similar advantage if you are a reader of the code: The visual and structural consistency supplied by the rules means you can focus your powers of understanding on the logic and the design of the code.
There is also an aesthetic argument. Beautiful things are largely consistent and harmonious. Where they deviate from consistency, those deviations signify something important. This is not about excluding creativity, but rather excluding it at certain levels in order to focus it on other levels.
Back to final. There is another motivation to adopt a rule, more specific to final. What does the absence of final mean to the reader of the code? Does it mean that the field will certainly be modified, somewhere? Or does it mean nothing, in which case, why ever use final? The meaning of final to the compiler is clear, and if final is absent, the compiler does not care. But human readers will look for meaning, and that meaning should be consistent.
So, we have to decide which rule to use: to never use final, or to use it whenever it can be applied. Taking all the above into account, here are my answers.
My earlier Java code never used final for fields (except for static final fields). Now I use it wherever it can be applied. I gradually became convinced that the benefits of final for fields were worth the additional visual clutter. If I don't put final on a field, it means that somewhere that field gets modified (or that I have accidentally omitted it; I correct such mistakes when I notice them). In this case the competing factors are quite closely balanced, which is why to took me a long time to decide to use final on fields, but on balance, they are a good thing.
As explained above, the benefits of final for local variables are less significant. Furthermore, local variable declarations (including method parameters) are much more frequent than fields. I estimate that at least 50% of local variables could take final. The visual clutter would be significant, for very little benefit. So, no final on local variables (except where required due to inner classes).