Your three counter-arguments boil down to:
1. Don't expect your team to be stupid/make mistakes.
2. Don't be stupid/make mistakes.
3. Don't write stupid/mistaken code.
I think we can all agree those are good points. The question is cost vs.
benefit of defending
against points where these break down. I find the cost of
automatically-inserted "final" to be
minimal (bordering on zero given an IDE), and the benefit to be
non-minimal/non-zero, and therefore
I go with it.
Let's take a particular case and break it down to see how "final" works out in
the end. Note that
parameter-processing methods like:
String myConcat(String left, String right) {
if ( left == null ) left = "";
if ( right == null ) right = "";
return left + right;
}
...can be just as well written as:
String myConcat(final String left, final String right) {
if ( left == null ) return myConcat("", right);
if ( right == null ) return myConcat(left, "");
return left + right;
}
This rewrite gives you a couple solid advantages. First, it's more expressive
of intent (although
that might be my functional programming-tinted eyes). To see the second
advantage, consider this code:
String myConcat(String left, String right) {
if ( left == null ) left = "";
if ( right == null ) left = "";
return left + right;
}
Did you notice the bug?
The result of the buggy "myConcat" will be an erroneous String consisting of
the characters 'n',
'u', 'l', 'l'. This will be kicking around and might well not cause an
explosion until some far
away point (probably after you've stored it in your database a bunch of times
for posterity). The
corresponding error symptom in the "final" case will be an infinite recursion
at the spot where the
error occurred, so the StackOverflowError will nicely point you right to where
the bug is, and you
won't have a chance to do something bad with the erroneous value. I consider
that a pretty huge win.
Now, a misguided and performance-obsessed developer might freak out because you
*may* have a couple
extra method calls and branches in there, but the reality is that the HotSpot
compiler will take
care of those cases in short order. Plus you're setting yourself up for
success when you reach a
JVM version which optimizes recursive tail calls.
This case demonstrates well my general experience with "final": it consistently
leads me to more
expressive and less buggy code. Removing "final" is usually a sign that I'm
trying to pull a stunt
that's going to get me into trouble, and often a sign of premature
optimization. Given that
experience, I'm really a fan.
More reasons I like "final" over on my blog:
http://enfranchisedmind.com/blog/2007/12/16/some-final-patterns/
Also, check out a before and after of my functional-y Java at:
http://enfranchisedmind.com/blog/2005/12/16/this-is-your-brain-this-is-your-brain-on-ocaml/
(Now, what's *really* interesting is that I don't use "final" in Groovy, and I
wouldn't even if it
actually worked exactly like Java's "final". There's a different sensibility
in Groovy than in
Java. Which is also why I use an IDE to write Java, but write Groovy in vi.)
~~ Robert Fischer.
Grails Training http://GroovyMag.com/training
Smokejumper Consulting http://SmokejumperIT.com
Enfranchised Mind Blog http://EnfranchisedMind.com/blog
Check out my book, "Grails Persistence with GORM and GSQL"!
http://www.smokejumperit.com/redirect.html
Reinier Zwitserloot wrote:
> This is a spin-off from the 'code reviews' thread (http://
> groups.google.com/group/javaposse/browse_thread/thread/
> 827d3257c903e9bd/35db9fd36906dd5d#35db9fd36906dd5d )
>
> Robert Fischer isn't the first soul I've heard mention that they
> either enforce setting all parameters to 'final', or, because that
> created ginormous method signatures, configure their IDE to warn/error
> when parameters are modified.
>
> I wonder if this really is a good idea.
>
> The usual arguments come in three flavours, at least as far as I've
> heard them:
>
> 1. The confusion argument: java is strictly pass-by-value so
> someParameter = x; does NOT change the meaning of 'someParameter' in
> the code that called your method. By disallowing assignment you avoid
> the confusion (This is Robert's argument).
>
> 2. The overlook argument: As methods grow larger (or even if they
> remain small and you're just glancing) you may miss a re-assignment to
> a parameter, and as you add some code onto the end of a method, for
> example to fix a bug, you erroneously think the value has been
> untouched. Proponents of this argument also usually enforce that
> 'return' must be the last statement, under the presumption that a mid-
> method return may let people think that the code they just added to
> the end is always run before the method returns normally.
>
> 3. The save-the-value argument: You never know when you need to
> original value that was passed in, so changing it may require
> refactors down the line.
>
>
>
> They all seem like bullpucky to me though. Point-by-point
> deconstruction:
>
> 1. If you're going to make rules because your fellow programmers don't
> know their arse from their teakettles, you'll never get anything done.
> Sure, for extremely obscure stuff I can see some value in setting up
> style checkers and the like to disallow certain operations, but
> something as simple as pass-by-value? Also, even if you really do have
> such mythically stupid programmers, you can still catch them in their
> error: Anybody that changes any local variable or parameter and then
> never touches it again is clearly doing SOMETHING wrong (that
> assignment was a no-op!), and if someone makes the mistake of thinking
> that java is pass-by-reference, they'll hit that sooner rather than
> later. javac doesn't check for this, but findbugs does. In fact, I
> think that most methods that would only work if java is pass-by-ref
> will make this mistake.
>
> 2. You can't just shove code into a method without understanding the
> method first. For example, I've seen lots of methods that take their
> incoming parameter, and then perform a normalization on it; for
> example, an incoming string gets a if ( x == null ) x = ""; treatment
> so future code can treat null and the empty string the same. Any
> casual method editor probably THINKS they are working on the
> normalized version. By enforcing the original coder to come up with a
> second variable to hold the normalized version, you're actually
> creating the very situation you tried to avoid!
>
> 3. ... then why don't you refactor it? We have refactor tools for a
> reason.
>
>
>
> I have a soft (meaning: feel free to break it if you have a decent
> reason to do so) rule regarding changing your method parameters:
>
> Make all changes to all parameters before the real meat of the method
> begins. Then, consider them final. (In other words, if you want to
> replace a parameter being 'null' with a default, or expand an input
> with a prefix, or any other normalization, do so at the very top of
> the method, then never change it again).
>
> Unfortunately there's no tool out there that has a rule to check if
> you adhere to it.
>
>
> >
>
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "The
Java Posse" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/javaposse?hl=en
-~----------~----~----~----~------~----~------~--~---