Monday, August 7, 2006

Code Advice #12: Use final on constructor and setter parameters

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


(See intro for a background and caveats on these coding advice blog entries.)



In my previous
code style entry I recommended a setter pattern where the parameter is identical to the field name, as in the following:


void setTemperature(int temperature) {
this.temperature = temperature;
}


I mentioned that there is the potential of a bug here if the parameter name has a typo; the code will compile just fine but not work as expected since you will be assigning the field back to itself.



There is another problem that can occur when you are "masking" the field with a parameter name. What if you add some code where you don't realize that the name is referring to a parameter, not the field? If you try to update the field, you will update the parameter instead:


void setTemperature(int temperature) {
this.temperature = temperature;
...
... // Other code here
...
if (temperature < -273) {
temperature = -273;
}
}

Ooops!



This motivates another good pattern (besides validating before assigning): Use the final modifier on parameters in constructors and setter methods:


void setTemperature(final int temperature) {
this.temperature = temperature;
...
... // Other code here
...
if (temperature < -273) {
temperature = -273; // Won't compile
}
}



The final modifier can be used in many other contexts as well. You can place it on fields to indicate that they are constants, not variables. You can place it on classes to indicate that they cannot be subclassed. You can place it on methods to indicate (and enforce!) that they cannot be overridden. In the old days, some programmers used to sprinkle final all over their code to get a performance benefit.
Be careful here - the performance benefit is typically not significant, and you can introduce some tricky bugs into your code. final should only be used on fields that truly are constant in a logical sense, not just on fields that happen to not be written to yet. This is especially true for public fields, because final fields get copied into clients that access the field. That's right - if you change the value of a constant, any clients that have already been compiled against a previous version of your class will continue to use the old value of the constants, until they are recompiled.




Using final on your method parameters on the other hand has few if any disadvantages. It does not show up in the signatures for your methods. It has no impact on anyone overriding your methods. It does not show up in the javadoc. It simply allows you to state the intent of parameters. And in constructors and in setter methods, parameters should reference state that is to be copied into the object. The references themselves should not be modified, and any attempts to do so will typically be bugs of the form shown above.



Static analysis tools can find the above bug too. PMD will flag cases where you are reassigning any of your parameters. Thus, it is making the assumption that all parameters are final. I've found that to be a bit too restrictive (for example, I sometimes want to adjust a parameter to a method based on some initial validation), so I don't like to run with that rule. Clearly it's easy to work with that assumption - rather than ever reassigning the parameter, copy it and work with the copy. However, marking parameters as final feels right - it's stating the intent of the parameter in setters and constructors, and stating intent is a good thing.



I'd like to get NetBeans to automatically use this pattern when encapsulating fields for me... Anybody listening?

P.S. Thanks to Rick for the inspiration for this entry.



(Confession: This pattern is a recent habit for me. We'll see if it stands the test of time.)


4 comments:

  1. I'm coming around on this pattern. It's logical and would certainly eliminate bugs I've personally dealt with in the past. My only objection is against the extra verbosity.
    However, perhaps these bugs are an argument for shortcut constructors and first-class property support? It's not a particularly elegant requirement that we Java developers have to explicitly define methods for dumb setters and getters, and that every needed combination of constructor initializers must be explicitly declared. In both cases, all we really want is to initialize or set some named field on the target object. It would seem that the bugs this "final" pattern solves are only a symptom of those missing features.

    ReplyDelete
  2. This is a great tip. In fact, I really, really enjoy reading (and trying to adhear to) all of your tips. This one, for whatever reason, got me to reply. Did you ever consider writing a small "Tor's Tips" manual?

    ReplyDelete
  3. Tor, you should know how it works ;) Don't wait for them to listen. Fill a new enhancement by yourself. http://www.netbeans.org/issues/enter_bug.cgi

    ReplyDelete
  4. Hi Tor,
    Thanks for the credit but I should have mentioned I got this tip from the OReilly book Hardcore Java.
    There is a whole chapter specifically focused on the ins and outs of the final keyword, whats more this chapter is available for (free) download, see:
    http://www.oreilly.com/catalog/hardcorejv/chapter/
    I litter my code with final's, and yes it's annoyingly verbose, but it can be a life saver!
    Good luck keeping the habit up.

    ReplyDelete