I ran into problems with someone doing this recently, so I’ll have to embellish the web a little more. The world must learn.
Why don’t we call non-final methods from constructors? Because it’s bad. Why is it bad? Because OO has an order to the madness and this ain’t the order. Let’s do a quick experiment to find out why.
Here is our parent and child class.
public class FooKlassParent{
static {
log("Parent: Init static block 1");
}
public SysWriter w1 = new SysWriter("Parent: non-static field init");
private static final SysWriter w2 = new SysWriter(
"Parent: static field init");
public FooKlassParent() {
super();
log("Parent: Constructor called");
doSomething();
}
static {
log("Parent: Init static block 2");
}
public void doSomething() {
log("Parent: do something");
}
protected static void log(final String msg) {
log(msg);
}
}
So many goodies! Static blocks, static fields, non-static fields, non-static methods. Yum. If you’re programming Java and don’t know the order these all fire in, please switch to PHP.
Now we’ll extend with a child who is essentially the same.
public class FooKlass extends FooKlassParent {
static {
log("Child: Init static block 1");
}
public SysWriter w1 = new SysWriter("Child: non-static field init");
private static final SysWriter w2 = new SysWriter(
"Child: static field init");
public FooKlass() {
super();
log("Child: Constructor called");
doSomething();
}
static {
log("Child: Init static block 2");
}
@Override
public void doSomething() {
// shame, you didn't call super.doSomething() first!
log("Child: do something");
}
}
Ok, now everyone pick up your pencils and write what will be the output of making a new FooKlass.
Parent: Init static block 1
Parent: static field init
Parent: Init static block 2
Child: Init static block 1
Child: static field init
Child: Init static block 2
Parent: non-static field init
Parent: Constructor called
Child: do something
Child: non-static field init
Child: Constructor called
Child: do something
Alright, so what did we learn? Things don’t “just happen” – Java operates by the JSR which is painfully specific here. The firing order is:
- Parent static blocks and fields in order of appearance
- Child static blocks and fields in order of appearance
- Parent non-static field initializers
- Parent constructor
- Child non-static field initializers
- Child constructor
Damn! That’s mind blowing! The child’s constructor calls super() but super() was really called before the child’s constructor. WYSINWYG. The byte code ain’t the code you wrote folks.
Now, to conclude, why are non-final method invocations from the constructor bad? The child class’s fields are not initialized yet. It’s state is not yet prepared. Even though we see the parent’s method, it’s been overwritten with the child’s byte code. Unholy things can happen now because your assumptions are no longer valid. And worse, it’s not just your assumptions, but someone might extend that lib/API class non-final public method. Mayhem will ensue, at 3AM, on Christmas Eve, when you’re on the support rotation and as you rub your eyes and stare into the debugger it just ain’t going to make sense to you.
Don’t do it.