As promised, here are a few useful cases for final in Java. In reality, there are so many of them that I’ve just gotten into the habit of using it unless I find a good reason not to: it just seems like the habit of using final makes ugly and buggy code more difficult to write, and since we are all our own coding horror, we need all the help we can get!
Stopping repeated String allocations.
We all know that it’s evil to write code like:
String s = "Hello";
[...]
s += ", ";
[...]
s += "World";
[...]
s += "!";
Well, if you use final, that code just became a lot more painful to write. You would have to do something like:
final String s1 = "Hello";
[...]
final String s2 = s1 + ", ";
[...]
final String s3 = s2 + "World";
[...]
final String s4 = s3 + "!";
Hopefully, about the time you were writing final String s3 =, it’d be occurring to you that you should probably be using a StringBuilder (or StringBuffer in the multithreaded case).
Being Certain That a Variable Is Initialized
This code is pretty common:
T out = null;
if(someTest(in)) {
out = value1;
} else if(someOtherTest(in)) {
out = value2;
} else {
out = value3;
}
doSomethingWith(out)
If you change T out = null; to just final T out;, the compiler will check that all code paths leading to “doSomethingWith(out)” will lead to “out” being assigned. And you can wipe out one more null in your code, which means one fewer opportunity for a NullPointerException to sneak out of your library and piss me off.
[Note: If you just wipe out the "= null;", the compiler may catch that it isn't initialized. The major advantages to using final are that it removes the temptation to add the "= null;" to get the error to go away, and that it forces the code to take a meaningful value attached directly onto the case -- much more readable (and therefore maintainable) code.]
Not Accidentally Assigning a Parameter
Whether explicitly stated or not, developers expect the parameters to be the same thing at the bottom of the method as they are at the top. I’ve bumped into bugs a number of times in my career when someone decided to get cute and use the parameter as a processing variable, only to surprise the maintainer (or some other developer) who expected it to be the same value as was passed in. So let Eclipse put “final” on your parameters and you won’t have to deal with that problem again.
Enforce a reality on your POJOs (a.k.a. “Death to JavaBeans”)
A lot of developers rely on coincidence when working with Java beans. Since the bean specification requires a no-argument constructor, setters have to get called to assign fields before any kind of processing methods can act on them. So you see lots of code like this:
MyBean b = new MyBean();
b.setFoo(foo);
b.doBar();
This creates a coupling which is almost always undocumented: an assumption that setFoo had to be called with a non-null value before doBar can be called. And it’s almost certain that doBar isn’t state-checking for foo to be null, which means that we’ve got a NullPointerException waiting to happen.
Worse, changes to that coupling relationship are difficult to track down and fix, particularly if the dynamic aspects of JavaBeans are being used — and, after all, that’s the whole point of following the bean specification! So your code is now less maintainable, its API less obvious, and the error reporting is poor.
I’d like to suggest a different direction, based on three rules:
- Any state which the POJO is assuming must be set in the constructor, and may not be changed afterwards.
- All fields for the object shall be
final, unless there is a damn good reason for them to be variable (this reason is usually the same reason the field istransient). - If there is a good reason for a field to be variable, any using code must check for
nullor other state violations immediately before use.
So, the code will now look like this:
MyPojo pojo = new MyPojo(foo);
pojo.doBar();
By having the variables be final, the compiler will guaranty that those fields are set before the constructor exits. Other state violations (like the field values not being null) can be checked once in the constructor, and then the developer can assume those things to hold true anywhere in the body of that code. This is much more maintainable and easy on the brain. The truly variable fields are discouraged by requiring extra work (i.e. a whole new branch that needs to be unit tested), which will often point out when you have a field that should be a return value. The user also gets a nice bonus, since a state issue with foo will result in a stack trace pointing to where the bad foo is erroneously passed in, not where-ever the bad foo happens to be first used. This makes your API a lot more self-documenting and easier to use. And, if your state assumptions change, the object’s constructor will change, which means that both you and your user will get wonderful compiler errors pointing out where your code needs to get updated. (Each of those was probably a NullPointerException — or worse! — before.)
Even better, this tends towards having objects that are immutable in their meaningful fields, which means the compiler can guaranty they are safe for use as hash keys, and we’re already 3/4ths of the way (or better) towards making them thread-safe.
An example of this kind of coding can be seen here.
Keeping Synchronization Sane
Peter Lawry’s comment on Hamlet’s “10 Best IDEA Inspections You’re Not Using” post brought up another good point.
Synchronizing on something other than final is a recipe for disaster.
See if you can spot the synchronization bug in this code:
Foo foo = FooFactory.getFoo();
MyBar bar = new MyBar();
synchronized(foo) {
if(bar.needsOverride()) {
foo = FooFactory.getFooOverride();
}
bar.mangleFoo(foo);
}
The problem is that Java synchronized on the object referenced by foo, but when foo changed to be a new reference, the synchronization stuck with the old referent. So if bar.needsOverride() returned true, then bar.mangleFoo(foo) was being called on an unsynchronized object and synchronization was blocking unnecessarily on an object that wasn’t even reachable in scope!
This example was pretty contrived, but I’m not the only one who has been saved by it in the past.
That’s about all I can think of for now…but that should be plenty!
4 Comments
Much appreciated food for thought. A niggling detail is that just because your parameters are final doesn’t mean they cannot be changed; we’re dealing with objects after all. So really if one is serious about being anal (!) then one needs to start out with immutable objects…
I’m actually a fan of functional programming and immutable objects. However, in an OO world, the data within an object might change out from under you, and that’s just the reality.
However, you can at least keep your maintainers from being surprised when the parameter variable isn’t the thing that was passed into the method. This is doubly important if you have certain checks (
nullbeing a popular one) that you’re assuming for passing in.The last example about synchronizing on a non-final field isn’t really contrived at all… There is no guarantee that all threads will see changes to non-final fields. So if thread A creates an object and sets the non-final field to a value in the constructor, there is no guarantee that thread B will see that value. It very well might do a read on the value and receive null. This happened to me last Fall and was quite reproducible (in that it happened maybe 1 in 10 times).
Also, I learned two weeks ago how nice immutable objects are when it comes time to optimize your Java code for performance. It opens up a lot of caching possibilities when you don’t have to worry about someone changing the state of your shared object.
Yeah, now if some clever person would just come up with a clever way of creating and caching immutable objects in a thread safe way, we’d be set!
3 Trackbacks
[...] Go back and take a look if you’re interested in Java development. Hat-tip to Hamlet d’Arcy’s “10 Best IDEA Inspections You’re Not Using” post for bringing yet another reason to use final to my attention. [...]
[...] the blog posting…Installing RMagick on Ubuntu: Problems I Encountered Doing “gem install RMagick”Some final PatternsStrongly Typed Languages Considered DangerousDefinitions of FreedomAnother Mason for Ron PaulMy [...]
[...] final everywhere — see Yet Another Reason final Is Your Friend. A ubiquitous use of final actually gave some nice patterns (in the “macro” sense of patterns), but raised all kinds of eyebrows and made my code [...]