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.
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.
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
・ ・ ・
(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.
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.
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