Abstract: The trend of marking parameters and local variables as "final" does not really enhance your code, nor does it make it more secure.
Welcome to the 207th issue of The Java(tm) Specialists' Newsletter, sent from the beautiful Island of Crete. Yesterday we went for a hike in the Stavros mountains, where the final crash scene of Zorba the Greek was filmed. We clambered up a hill and were able to see Milos, over a hundred kilometers away. We only recently started exploring the hills behind our house and have already found quite a few nice places with fantastic views. Very few people walk there, so we pretty much have the place to ourselves. Maybe it will be more busy in summer.
javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.
Java 1.1 introduced the inner class, including the anonymous version. Since they did not really want to change the class format, they compiled these inner classes as normal classes with accessor methods for the privately accessed fields and methods. Thus the following class:
public class Foo { public class Bar { } }
Would result in two class files, Foo.class and Foo$Bar.class:
public class Foo { public Foo() { } } public class Foo$Bar { final Foo this$0; public Foo$Bar(Foo foo) { this$0 = foo; super(); } }
We could also define new classes inside methods, for example:
public class Foo { public void baz() { class Bar { public String toString() { return "I am a bar!"; } } Bar bar = new Bar(); System.out.println(bar); } public static void main(String[] args) { Foo foo = new Foo(); foo.baz(); } }
This would be compiled to again two classes, this time to something like Foo.class and Foo$1Bar.class. The naming convention could change by compiler, since Bar is only visible inside the method. However, Bar is not an anonymous class in this case. Instead, it is a local class, meaning that it has an enclosing method, which you can determine with the getEnclosingMethod() call. If it was defined inside a constructor, you could get that with the getEnclosingConstructor() call. Local classes are rare. In the JDK 1.7.0_09, I counted only 23, whereas there were 12987 normal classes, 2449 anonymous inner classes and 4341 non-anonymous inner classes. I would wager a beer that in most bodies of code, you would also find the local class to be a bit of a rarity.
Here is what the compiler would typically generate:
class Foo$1Bar { final Foo this$0; Foo$1Bar(Foo foo) { this$0 = foo; super(); } public String toString() { return "I am a bar!"; } } public class Foo { public Foo() { } public void baz() { Foo$1Bar bar = new Foo$1Bar(this); System.out.println(bar); } public static void main(String[] args) { Foo foo = new Foo(); foo.baz(); } }
If we instead defined an anonymous inner class, it would look like this:
public class Foo { public void baz() { Object bar = new Object() { public String toString() { return "I am an anonymous bar!"; } }; System.out.println(bar); } public static void main(String[] args) { Foo foo = new Foo(); foo.baz(); } }
The generated classes would now typically be called Foo.class and Foo$1.class:
class Foo$1 { final Foo this$0; Foo$1(Foo foo) { this$0 = foo; super(); } public String toString() { return "I am an anonymous bar!"; } } public class Foo { public Foo() { } public void baz() { Object bar = new Foo$1(this); System.out.println(bar); } public static void main(String[] args) { Foo foo = new Foo(); foo.baz(); } }
In Java, parameters are always passed by value. When I pass a primitive type to a method, this method is able to change its copy without affecting the variable that was passed in. If we pass in an object reference, we are able to change the reference inside the method without the outside reference changing. In a few other languages such as Pascal you could pass in parameters by reference. In C you could pass in a pointer. In Java, the following would not have any effect:
public void increment(int i) { i++; }
Now let's get back to the inner class construct. When we define an anonymous inner class, the parameters and local variables that it is allowed to see have to be defined as final. Thus the following would not compile:
public void show(int i) { new Runnable() { public void run() { System.out.println(i); } }.run(); }
Instead, we would need to make the parameter i final, which would make it visible and stop us from accidentally modifying it in the inner class. Once we have done that, the anonymous inner class would look something like this:
class PassByValue$1 implements Runnable { final int val$i; final PassByValue this$0; PassByValue$1(PassByValue passbyvalue, int i) { this$0 = passbyvalue; val$i = i; super(); } public void run() { System.out.println(val$i); } }
So this is the reason why we have the option since Java 1.1 to mark local variables and parameters as final.
A few years ago, someone started the trend of marking all their local variables and parameters as final. The first time one of my customers insisted on this was end of 2004. Even though I thought it was an idiotic coding standard, it took me about five minutes to apply using IntelliJ IDEA's Analyze/Inspect Code function. In retrospect, I should perhaps have punished them with punitive billing by charging them for how much this would have taken me with a lesser IDE. After I applied the changes and handed over the code, I found another 100+ places in their code where they had not marked local variables and parameters as final.
I like to compare this trend to tattoos. When I was a kid, gangsters, prisoners and seamen sported tattoos. As with most fashions, it suddenly became the cool thing to have a tattoo as it demonstrated a streak of rebellion and going against the social order. However, this became so pervasive that anybody without a tattoo can today be considered a rebel. Now if you are one of those that feel the need to decorate your skin with paint, please don't let me cramp your style. Go ahead, it's the fashion and everyone else is also doing it. But I don't like it. I personally don't feel that it is an enhancement.
Similarly, marking all your local variables and parameters as final is a trend that does not enhance your code in my opinion, but just clutters it with unnecessary text. I would prefer to read:
public void printNames(List<String> names, int maxNames) ...
rather than:
public void printNames(final List<String> names, final int maxNames) ...
As a user of your library, I don't want to know or care that you have decided to not change the parameters inside the method call. Since the arguments are passed by value anyway, it won't affect my life one little bit if you decided to use maxNames as a counter variable rather than define your own.
A recent book on Java concurrency used final parameters for all their code samples and it was rather unreadable as a result. I won't mention the title of the book as I didn't find it particularly good and would not want to promote it.
I am of the opinion that you should not generally not change parameters inside a method. But I don't need the compiler to tell me this. That is how I code anyway. Similarly, I try to not change local variables. In fact, I avoid using local variables where I can.
I understand that you might feel differently about this.
Note that I am not against marking fields final, as this has some useful concurrency semantics. I am also not against making methods and classes final if that is going to help to guide users of your classes on how they should be extended. This is only a rant against marking local variables and parameters as final, when it is not necessary to do so.
Kind regards
Heinz
We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)
We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.