[Java] Use of final in local variable declaration

Is final required to declare local variables?

I basically don't use final in my own functions. I think the reason is simply "I wrote it so that you can see it." For example, it has the following form.

public static int f(int x) {
  int y = g(x);
  int z = h(y);
  return z;
}

When you look at this, it's self-evident that y and z (and x) aren't reassigned. And since the function I define is usually a few lines like this, I feel that it is redundant to add final when declaring it, so I try not to add it.

However, I think it's okay to have another. For example, if you set the IDE settings to automatically add final, at least it will not affect the number of types, and I think you will soon get used to it. In short, in the above cases, we are not so particular about it.

Situations that use final

Well, if you don't use final at all, that's not the case, and in some cases it may be used. Below, I will introduce a case where I personally use final.

1. Declaration in a long function

Since the above function was defined by myself, it only took a few lines, but when modifying it in actual business, variables may be declared in a very long function (hundreds of lines, a thousand lines, etc.). Within such functions, it is not uncommon for there to be tens or hundreds of lines between the variable declaration and where the variable is used. At that time, if final is not attached at the time of declaration, it is necessary to read all the lines in between and grasp the latest value.

line number
100      public static int longLongFunc() {
101        String foo = "foo";

~~ Omitted ~~

           //You need to read the 400 lines in between to know the value of foo at this point.
500        String bar = hoge(foo);

・ ・ ・

Alternatively, the scope continues after the variable is used, so the value does not necessarily change in the lines below it.

           //It may be reused like this(What is initialization ...)¡
700        foo = null; //Initialization
701        foo = fuga();

・ ・ ・

Of course, it may be a story that can be prevented by code review, but in the real world, it is not uncommon for companies to have no such culture.

The above cases can be dealt with by adding final when declaring.

line number
100      public static int longLongFunc() {
101        final String foo = "foo";

~~ Omitted ~~

           //The value of foo has not changed since it was declared("foo"As it is)Therefore, it is not necessary to grasp the processing in between.
500        String bar = hoge(foo);

~~ Omitted ~~

           //Cannot be reused in the line below as it will result in a compile error
700        foo = null; //Compile error


・ ・ ・

2. When setting a value according to the conditions

(19/10/1 postscript / correction) Mr. @Kilisame pointed out in the comments, and I found out my big misunderstanding, so I am making a major correction. Java local variables, regardless of the presence or absence of final, will cause a compile error if the initial value is not assigned only by the declaration, but in the case of only the declaration, the implicit initial value is assigned like the field. I misunderstood it. Thank you for pointing out, @Kilisame.


Conditional branching also often uses final. For example, consider the following case.
String hoge = "";
if (isFoo) {
  hoge = "foo";
} else if (isBar) {
  hoge = "bar";
}

If you just don't want to reassign, you can also use the conditional operator (ternary operator) to write:

String hoge = isFoo ? "foo" : isBar ? "bar" : "";

It's not impossible to write, but it's certainly hard to see if else if is mixed, not to mention if-else. Basically, it is better to avoid nesting of conditional operators. In such a case, you can also write as follows.

// 19/10/1 Comment out for correction(See additional notes)
// final String hoge; //Declaration only

String hoge; //Declaration only
//Substitute a value according to the condition
if (isFoo) {
  hoge = "foo";
} else if (isBar) {
  hoge = "bar";
} else {
  hoge = "";
}

Since ~~ final is "a value that can be assigned only once", the timing of declaration and assignment itself can be different. Instead, you will get a compile error if you don't set a value somewhere before it goes out of scope. The advantage of the above writing is that you can prevent oversight of cases where no value is set. For example, the following example will result in a compile error. ~~

(19/10/1 postscript / correction) Even if there is no final, a compile error will occur if the initial value is not assigned only in the declaration.

// 19/10/1 Comment out for correction
// final String hoge;

String hoge;
if (isFoo) {
  hoge = "foo";
} else if (isBar) {
  String fuga = getFuga();
  if (!fuga.isEmpty()) {
    hoge = fuga;
  } //If this is false, no value will be set, resulting in a compile error.
} else {
  hoge = "";
}

In order to compile, it is necessary to cover all cases as follows.

// 19/10/1 Comment out for correction
// final String hoge;

String hoge;
if (isFoo) {
  hoge = "foo";
} else if (isBar) {
  String fuga = getFuga();
  if (!fuga.isEmpty()) {
    hoge = fuga;
  } else {
    hoge = "bar";
  }
} else {
  hoge = "";
}

~~ As a little more supplement, remove the final of the above example that causes a compile error and substitute the initial value. ~~

(19/10/1 postscript / correction) Since final is removed, only the initial value is substituted.

String hoge = "";
if (isFoo) {
  hoge = "foo";
} else if (isBar) {
  String fuga = getFuga();
  if (!fuga.isEmpty()) {
    hoge = fuga;
  } //When this is false, hoge is""as it is.
}

In this case, the compilation itself will pass. However, this may be because you forgot to write the case where fuga.isEmpty () is true (of course, it may be as expected).

String hoge = "";
if (isFoo) {
  hoge = "foo";
} else if (isBar) {
  String fuga = getFuga();
  if (!fuga.isEmpty()) {
    hoge = fuga;
  } else {
    hoge = "bar"; //Maybe I was planning to write like this
  }
}

This kind of bug is also a common bug in practice. People are creatures that make mistakes. Therefore, it is safer and more secure to write in such a way that a compilation error will occur if you make a mistake, rather than writing in such a way that compilation will pass in both cases of mistakes and in other cases. ~~ If you forget to write the above example, you can prevent unexpected bugs by adding final. ~~

(19/10/1 postscript / correction) The guideline itself, "If you make a mistake, you should get a compile error" does not change, but the above example is inappropriate because it does not relate to the presence or absence of final. Regarding the merits of adding final in such cases, @Kilisame pointed out and illustrated and explained in the comment section, so please refer to that.
In addition to the above example, I think it is also good to make the following function and call it.

//If you make it a function, Ring hoge= getHoge();Can be written
public static String getHoge() {
  if (isFoo) {
    return "foo";
  } 
  
  if (isBar) {
    String fuga = getFuga();
    if (!fuga.isEmpty()) {
      return fuga;
    } else {
      return "bar";
    }
  }
  return "";
}

I would like you to think about whether to make it a function on a case-by-case basis.

Summary

Currently, I don't add final to all local variables, but I'm consciously writing "don't worry if I add final" to all local variables. Rather than whether or not to add final, if you are properly aware of the background and purpose of adding final, such as "preventing unexpected bugs" and "easily repairing", it may not turn in the wrong direction. I am thinking.

Or if it is team development, I think that the strategy of forcing final in the coding standard and ensuring only the minimum quality without the awareness of the programmer is also an ant.

This article wasn't about "whether or not to use final", but I just wanted to introduce a case where "it's more convenient to use final in such cases", so you or your team can decide what to do. I hope you can think about it.

Thank you for reading for me until the end. If you have any questions or deficiencies, please contact us in the comments section or Twitter.

Recommended Posts

[Java] Use of final in local variable declaration
java variable declaration
About var used in Java (Local Variable Type)
Use OpenCV in Java
Use PreparedStatement in Java
Why use setters/getters instead of public/private in Java
Use of Abstract Class and Interface properly in Java
About Java variable declaration statements
Implementation of gzip in java
Use Redis Stream in Java
Implementation of tri-tree in Java
Java variable declaration, initialization, and types
[Java] Do not use "+" in append!
Use composite keys in Java Map.
How to write Java variable declaration
List of members added in Java 9
How to use classes in Java?
List of types added in Java 9
Implementation of like function in Java
Do you use Stream in Java?
[Java8] Proper use of Comparable and Comparator in terms of employee sorting
[Java] I participated in ABC-188 of Atcorder.
Implementation of DBlayer in Java (RDB, MySQL)
Get the result of POST in Java
Multilingual Locale in Java How to use Locale
Use OpenCV_Contrib (ArUco) in Java! (Part 2-Programming)
Create variable length binary data in Java
Difference between final and Immutable in Java
Use static initialization block to initialize List / Set of static fields in Java
[Java] Use cryptography in the standard library
Use "Rhino" which runs JavaScript in Java
What wasn't fair use in the diversion of Java APIs on Android
[Java] [Maven3] Summary of how to use Maven3
The story of writing Java in Emacs
Role of JSP in Web application [Java]
Discrimination of Enums in Java 7 and above
[Java] Why use StringUtils.isEmpty () instead of String.isEmpty ()
Use jenv to enable multiple versions of Java
The story of low-level string comparison in Java
[Java] Handling of JavaBeans in the method chain
The story of making ordinary Othello in Java
About the idea of anonymous classes in Java
[Android / Java] Operate a local database in Room
The story of learning Java in the first programming
[Java] use Optional instead of if (hoge! = null) {...}
Feel the passage of time even in Java
Dynamically change private static final fields in Java
Basics of threads and Callable in Java [Beginner]
A quick review of Java learned in class
How to use JQuery in js.erb of Rails6
[JAVA] [Spring] [MyBatis] Use IN () with SQL Builder
What I've always been interested in in Java final
Method name of method chain in Java Builder + α
[Java] Difference between "final variable" and "immutable object"
Import files of the same hierarchy in Java
Initialization timing of member variable with final specified
Partization in Java
Rock-paper-scissors in Java
Java variable scope (scope)
Java variable scope
[Java] [Maven] Template of pom.xml to register 3rd party jar file in local repository