I work on a large, mainly Java codebase. If we used `final` everywhere it is applicable, the code would have a ton of visual noise. We find that `final` is not as valuable in most cases.
`final` doesn't buy you as much in Java as `const` does in C, or (the lack of) `mut` in Rust.
In Rust, I can pass in an object reference without mut, and I know that the object will be the exact same after the function call.
In Java, even if a function takes `final ArrayList<..> xs`, the function can still delete every element of your array, modify elements at will, etc.
That means `final` is basically useless as far as using an API goes (I don't care if the implementation of a function reassigns to the same variable as the object I'm passing in, but I do care if they mutate it).
`final` only helps you avoid dumb reassignment bugs that tests should probably catch anyway, and in that case, if you are already writing good tests, why pollute the code with the visual noise of `final`?
As for "visual noise," I find it helps to highlight variable declarations and results in more readable code, but we're firmly in the personal opinion area here.
* Member variables of a class that should be initialized in the constructor
* Static final constants
* Any configuration-like variable
* Like you mentioned, when you need to initialize a variable in a particular scope, but the initialization could happen in several control paths of inner scopes
* Function parameters.
Re-assigning a variable used as a function parameter is frequently a mistake, and almost always makes code more confusing. I find the "this is defined only once in this function" philosophy to be useful in keeping complexity down in Java programs. Even if final doesn't make objects immutable.
Quite true.
> `final` only helps you avoid dumb reassignment bugs that tests should probably catch anyway ...
There is another reason for declaring parameters `final` in Java. It is the only way[1] which they can be used in an anonymous class definition such as:
return new SomeClass () {
override int addOne () {
return aNamedOuterMethodParam + 1;
}
}
1 - "The only way" as of Java 7. Newer versions may have removed this restriction, I haven't bothered to check.(Obviously you know that, Steve, but it's the sort of thing that I find a lot of Java folks mentally don't get.)
If you are working with code where you know that functions never modify their parameters then it is simpler to reason about what it does. I prefer to write code this way myself if there is no language mechanism to enforce it.
Other possible uses of `final` (to tag methods or classes) have the downside of reducing the flexibility of code by removing interfaces / "seams" that can be overridden. This reduction of flexibility will generally make things harder to test, which may or may not matter. Michael Feathers has a short article discussing this [1].
[1] Michael Feathers' "Testable Java" : http://www.objectmentor.com/resources/articles/TestableJava....
For built-ins, such as numeric types and "char" (String is obviously disallowed from this definition), final will prevent changes to instances of those types similar to what "const int" does for C++.
For pointers, however, the final keyword in Java limits the ability to change the variable holding the pointer, not to what it references. The equivalent in C++ would be:
SomeClass * const foo = &instanceOfSomeClass;
Here, the pointer "foo" could not change, but operations changing foo would be allowed.Java (the language) does not possess the semantics to express "the pointer given to a method cannot allow changes to what it points."