Java annoyance: final parameters

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.

6 thoughts on “Java annoyance: final parameters

  1. [In C++, const] provides the caller with a guarantee that the function will not modify the string

    The guarantee is that the function will not modify the string through the const pointer. It’s allowed to modify the string by other means, e.g. by casting the pointer so as to remove the const qualifier.

  2. True. Which goes back to why Java wanted to hide pointers from programmers.

  3. Am I the only one who thinks Java should have something like C++’s Const concept?

    The ‘final’ word reffers to the Reference, so the ‘const’ word should reffer to the Value.
    Why? Because one of the Java’s main concept is that some errors *must* appear in compile time.

    Imagine:
    public void alterObj(User user){…}
    public String readFromObj(const User user){…}

    class User{
    public String getName() const;
    public String setName();
    }

    Sure, this would not be compatible with current sources, I know.
    But that’s somthing I miss from C++.

  4. Yeah, what Gareth said. In fact, in C (and C++?) const isn’t their for the benefit of the function being declared, it’s their for the benefit of callers. To avoid them having to insert essentially pointless deconsting casts.

  5. Ah, you enterprise programmers. Your “Object c = x;” example is fine if you have terabytes of storage for your bytecodes.

    On MIDP devices it will result in additional bytecodes (hence larger JAR files). This is annoying because semantically there’s no difference and any half-decent compiler should generate identical code. But the actual compiler we use in the real world generates larger code. This what happens when you try and make up for a stupid compiler by having a clever hotspot compiling JVM.

Comments are closed.