Blog-Archiv

Samstag, 13. Februar 2021

Java Local Final Variables Make Sense

I have not been using the final Java keyword when I was an inexperienced software developer. This was a mysterious keyword to me for a long time. Especially for local variables inside a method, I asked myself: why would immutable values make sense? There are opinions on the web proving that I am not the only one asking this question.

In this article I will present some code snippets that may shed a little light on immutability. It is not easy to understand why we should use final as much as possible. The realization comes only over time when you have seen a lot of the buggy code that is around everywhere. Long methods are the home of mutability mistakes.

A User Story

The Java source code below implements the following story:

I want to go walking when
  • it is not raining, and
  • either it is not cold or no wind is blowing
and
  • a park is in reach

Seems to be an application for travellers ...

Buggy Implementation


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    private boolean shouldGoWalking(
            boolean raining,
            boolean cold,
            boolean windBlowing,
            Park[] parks)
    {
        boolean goWalking = false;
        
        if (raining == false && (cold == false || windBlowing == false))
            goWalking = true;
        
        for (Park park : parks)
            if (park.isInReach())
                goWalking = true;
        
        return goWalking;
    }

It is easy to see that this code doesn't work as expected. The problem starts at line 12. When a park is in reach, the weather simply is ignored. S... happens.

How can we detect such bugs quickly? You got it: make everything final (even the parameters). I wouldn't even try to understand the code before doing this.

7
        final boolean goWalking;

Consequence is that you can assign a value just once. The compiler will show you immediately all places where the variable is assigned, and there you have the bug.

Correct Implementation


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    private boolean shouldGoWalking(
            final boolean raining,
            final boolean cold,
            final boolean windBlowing,
            final Park[] parks)
    {
        final boolean acceptableWeather = 
            (raining == false && (cold == false || windBlowing == false));
        
        final boolean parkInReach = Arrays.stream(parks).anyMatch(p -> p.isInReach());
        
        return acceptableWeather && parkInReach;
    }

I made two steps in one:

  1. give all involved logics descriptive names: acceptableWeather and parkInReach
  2. make all local variables constant through the final access modifier

This is also much better readable code.

Most mutability bugs I've seen happened with String and boolean variables.

Conclusion

Actually it is quite easy to realize the value of immutability:

  1. functional languages do not allow mutable variables
  2. functional languages deliver the most robust implementations you can get

If you sum this up, you find out that you should use as much immutable class fields, parameters and local values as possible. The final keyword does not cutter code. It proves that you were thinking while programming.