Tuesday, August 9, 2022

final'ly

In Java the final keyword can be applied to variables, classes, fields and methods. But why should we use the final keyword? Isn’t it just more noise and code bloat? I believe the answer is No!, final is an important tool in the code readability toolbox. Remember, we write code once, but we read it hundreds of times.


Class

Final classes are defined in the language specification here: https://docs.oracle.com/javase/specs/jls/se18/html/jls-8.html#jls-8.1.1.2

The primary use is to prevent subclassing. This “is useful when the class hierarchy is used to model the kinds of values in a domain, rather than as a mechanism for code inheritance and reuse”.

In other words, when a reader encounters sealed or final classes, they know that this is a complete implementation, and they do not need to search the code base for extensions. This is particularly useful when performing code reviews.

Method

Final methods are defined in the language specification here: https://docs.oracle.com/javase/specs/jls/se18/html/jls-8.html#jls-8.4.3.3

The primary use is “to prevent subclasses from overriding or hiding it (the method)”. This is a useful tool for the code writer to limit and constrain access to class internals - though arguably extracting a final class may simplify reading and understanding the code in future.

More interesting though, is the specification (unusually) points out an optimization benefit: “At run time, a machine-code generator or optimizer can ‘inline’ the body of a final method”. This has the usual inlining performance benefit, but also allows the runtime optimizer to perform enhanced optimizations as it can now ‘see’ a larger surface area to optimize over.

Variable

Final variables are defined in the language specification here: https://docs.oracle.com/javase/specs/jls/se18/html/jls-4.html#jls-4.12.4

Final variables may only be assigned to once. This “can serve as useful documentation that its value will not change and can help avoid programming errors”. 

What class of programming error is being prevented here? Most of the bugs I’ve personally found and fixed through enforcing final variables have been of the scoping variety (assigning a value to a variable instead of a field e.g. name=name instead of this.name=name).

I have also found that enforcing final variables has helped me avoid mutating state unnecessarily, through questioning why I needed to mutate certain state. This has led to me writing code that is significantly easier to test without the use of mocks. (ymmv).

Final variables can reduce the cost of garbage collection (by reducing the need for memory barriers - you don’t need to protect an address from writes if it cannot change). Final primitive variables can also lead to some significant performance improvements. They allow the compiler to perform static (compile time) optimizations it could not otherwise do.

Field

Final fields are defined in the language specification here: https://docs.oracle.com/javase/specs/jls/se18/html/jls-8.html#jls-8.3.1.2

Final fields “must be definitely assigned ... at the end of every constructor of the class in which it is declared”. I find this tremendously helpful in preventing bugs where folks forget to set fields. Consider using the JDK 14 added Record class which is a special case of this where you only use the constructor and getters.

Final fields have similar benefits as final variables and methods.

No comments:

Post a Comment

Dependency Injection

  After many years of practicing dependency injection via many different frameworks (Spring, Guice, Dagger to name a few), I've decided ...