First, the summary:
Q: What does it mean when a method I’m calling declares that its parameters are final?
A: To you, nothing. It’s a safety feature for the author of the method.
Q: OK, so when should I declare method parameters as final?
A: Absolutely always.
Q: Isn’t that just annoying busywork?
A: Pretty much so, yes. You must be new to Java.
And now, the lengthy discussion.
Way back in the days when the closest thing to a decent programming language available to me was C or Pascal, I disliked functions with side effects. At the time they mostly seemed to me to be a nasty hack to get around the fact that there was no easy syntax for returning multiple values.
Today, I’m even less of a functional programmer than I was in college; but I still see functions with side effects on their arguments as evil. Sometimes a necessary evil for performance reasons, but evil nevertheless. (Obviously side effects on an object by its own methods aren’t anywhere near as big of a problem, though I like that Ruby distinguishes them from side-effect-free methods by a special naming convention.)
So my deeply ingrained habit is to treat function/method parameters as unmodifiable, whatever the programming language. I had actually forgotten that this was a habit until I started running PMD on my Java code, and promptly had it warn me that every single method parameter ought to be declared final
. This in turn made me think about Java’s method call semantics, and how badly chosen and useless the word “final” is on method parameters.
In C and C++, the const
keyword is used all the time when library functions accept string parameters, and provides the caller with a guarantee that the function will not modify the string. For example:
int strcmp(const char *s1, const char *s2);
You might naively expect that in Java, final
performs a similar function — but it doesn’t really.
First off, in Java all Strings are immutable. So whether arguments are final or not, your strings will never be modified by being passed to a method.
Secondly, in Java all objects are passed as a value consisting of a pointer to the object. The Java Thing a
is really like C++ Thing *a
, and Java’s a.method()
is really C++’s a->method()
, not a.method()
.
To look at it the other way, if you could have an actual object in a variable x, Java’s foo(x)
would really be doing a C++ foo(&x)
— but in Java, you never get to touch the actual object x.
It seems that the designers of Java wanted to make sure nobody confused their object pointers with the evil manipulable pointers of C and C++, so they decided to call them references instead. So Java books usually say that objects are stored as references, leading people to think that objects are passed by reference. They’re not, they’re passed by value, it’s just that the value is a pointer. If you’re confused again, try going back a couple of paragraphs.
Anyhow, the upshot of pass-by-value with objects represented by pointer values, is that as a caller of a Java method, the presence or absence of final
on parameters tells you absolutely nothing. Even if the parameters are all final, the method can still modify the value of any objects you pass in, unless the objects themselves are special immutable ones like the String class. Contrariwise, even if the parameters aren’t final, the method can’t make your variables end up pointing at a different object.
This is undoubtedly why JavaDoc ignores final
on parameters when generating documentation.
What about the value to the author of the method? Well, reassigning parameters is pretty much universally viewed as a bad thing to do, and Eclipse will warn you about it.
If you’re not convinced it’s unwise, consider the case where you have a constructor with a parameter ’name’ that gets set as the initial value of a field, also called ’name’. If parameters are final, there’s no way to accidentally modify the parameter when you think you’re modifying the field. Yes, Eclipse warns about parameters masking fields too, but still… more safety is good if the cost is low.
So let’s consider what would happen if we made all parameters final
by default.
There may be a rare occasion where you genuinely need to change values passed in to a method before performing some computation. Since every object in Java is manipulated as a pointer, it’s computationally trivial to introduce a temporary variable that’s a copy of the parameter, and then either change it to point to a different object or not. So instead of
void foo(Object x) {
if (x == null) { x = SOME_DEFAULT; }
...
}
you’d have to do
void foo(Object x) {
Object c = x;
if (c == null) { c = SOME_DEFAULT; }
...
}
No big problem.
How about performance? Well, it turns out that the final-ness of parameters isn’t part of the method signature. I also checked, and the compiled class files are byte-identical whether your parameters are final or not. So, no performance benefit either way.
Finally, no pun intended, the use of final
on parameters is required when using inner (nested) classes for listeners or iteration. This crops up all the time in Swing programming, and a Google search shows that it’s a common cause of intense confusion.
So I don’t see any value to making the final-ness of parameters a matter of choice for method authors either. They should all be final, always. You won’t see any significant performance change, but you’ll get a bit more safety, and you’ll be a bit less likely to encounter a weird error message when attempting to use listeners and iterators.
So the actual word final
on method parameters is just noise, providing no benefit to API users, programmers with good coding habits, or anyone using a tool like Eclipse that warns about reassigning parameter variables. The language should have made final parameters at least the default, and ideally the only option. It annoys me that I end up with my method declarations cluttered with final
, final
, final
.