|
The Java Specialists' Newsletter
Issue 094 2004-09-20
Category:
Language
Java version: Sun JDK 1.5.0-rc Java Field Initialisationby Dr. Heinz M. Kabutz
Welcome to the 94th edition of The Java(tm) Specialists' Newsletter. I would like to
welcome Nelson Boegbah from Liberia onto our list (we now
have readers from 101 ). Can you imagine
a city of one million residents, without any running water,
electricity or telephone lines? I certainly cannot imagine
that, but that is what Monrovia (capital of Liberia and
Nelson's home) is like. Nelson has promised to write a few
stories about life in Liberia. They are off-topic for our
Java newsletter, so if you are interested, please send me an
email, and we can start a Life-In-Liberia newsletter :-)
August 2004 was extremely busy. I spent my days and nights
developing a J2ME application for a customer. It
reminded me of my PhD days - working until 3:00am every
morning (I'm sure you can relate to that ;-). The highlight
was one Friday night, when I sneaked into bed at 1:00am, and
felt guilty because there were still two productive hours
left in the day! As you may imagine, there was not too much
time left over to write newsletters, but I AM BACK!
This newsletter was written based on an idea sent to me by
Remco de Boer from the Netherlands.
Attention: On the 30th of November 2004, I would have
been sending The Java(tm) Specialists' Newsletter for exactly four years. I want to try
publish the 100th newsletter on that same day. If you have
good ideas that you would like to share with 8000+ Java
specialists in 101 countries, please email them to me. I
want to thank all those who have helped grow this newsletter
over the past 4 years by forwarding it to their friends and
colleagues :-)
Java Field Initialisation
The idea is to have two classes, an abstract superclass and
a subclass implementation. The subclass' constructor calls
the superclass constructor, which in its turn calls an
abstract method implemented by the subclass. The method
implementation sets a (subclass) member variable - and this
is where the fun begins.
I have often seen code like this:
public class MyClass {
private boolean b = true; // unnecessary initialisation
private int i = 42; // unnecessary initialisation
public MyClass(boolean b, int i) {
this.b = b;
this.i = i;
}
}
The writer of MyClass was being overcautious, by initialising
fields that are actually being set in the constructor anyway.
"Oh, but it does no harm." Really? Before we look at
Remco's example, let us decompile the class and see what the
compiler did with the field initialisers:
public class MyClass {
public MyClass(boolean flag, int j) {
b = true;
i = 42;
b = flag;
i = j;
}
private boolean b;
private int i;
}
If we look carefully, we see that the field initialisers
get copied into the constructor as part of compilation.
The steps "b = true" and "i = 42" are thus of no use at all.
I find this interesting. All the initialising code and the
initialiser blocks, are copied into each of the constructors.
Another quick example:
public class MyClass2 {
{ System.out.println("Goodbye"); }
public MyClass2() { }
public MyClass2(boolean b) { }
public MyClass2(boolean b, String s) { }
private int i = 4;
{ System.out.println("Hello"); }
}
becomes the compiled class:
public class MyClass2 {
public MyClass2() {
System.out.println("Goodbye");
i = 4;
System.out.println("Hello");
}
public MyClass2(boolean flag) {
System.out.println("Goodbye");
i = 4;
System.out.println("Hello");
}
public MyClass2(boolean flag, String s) {
System.out.println("Goodbye");
i = 4;
System.out.println("Hello");
}
private int i;
}
Now, let us look at the classes that Remco sent me:
public abstract class A {
public A(int i) {
build(i);
}
protected abstract void build(int i);
}
and its subclass
public class B extends A {
private int size = 0;
public B(int size) {
super(size);
}
protected void build(int size) {
this.size = size;
}
public int size() {
return size;
}
public static void main(String[] args) {
B test = new B(1);
System.out.println("Size: " + test.size());
}
}
The resultant output is:
Size: 0
Correct, but it is easy to think that the output should
rather be "Size: 1", but it is "Size: 0"! You can prevent
this by not explicitly setting the B.size field to 0 in the
declaration. In other words, declaring
private int size; provides the
expected answer ('Size: 1').
According to the JVM Language Specification, this is expected
behaviour. A superclass is initialized before the member
variables of a subclass. Explicitly setting the member
variable to 0 therefore takes place after the super
constructor called the build method (note that you can set
the size to anything you want, not necessarily 0). When you
leave out the explicit '= 0', the variable is of course still
(implicitly) initialized to 0. However, this implicit default
initialization ('preparation') is performed before the
superclass constructor. [See for instance section
2.17.6, in particular steps 3 and 4].
It makes sense, when we consider that the initialisation
code (size=1) is moved to the start of each constructor.
final: The confusion could have been
avoided by changing the design of the application. I usually
try to make all fields final. This
would have highlighted the problem in the code from the
start.
Kind regards
Heinz
Language Articles
Related Java Course
Discuss at The Java Specialist Club
|