|
The Java Specialists' Newsletter
Issue 207 2012-12-27
Category:
Language
Java version: Java 1.1 - 8 Final Parameters and Local Variablesby Dr. Heinz M. KabutzAbstract: 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.
Thanks for reading this newsletter on our website. We also have a mailing list. That is where the real action takes place (webinars, free reports, etc.). Maybe subscribe today?
Advanced Java Courses on Crete:Java Specialists Master Course 18-21 June 2013 and
Concurrency Specialists Course 6-9 August 2013.
Final Parameters and Local Variables
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
Language Articles
Related Java Course
Discuss at The Java Specialist Club
|