Sunday, September 9, 2012

Super constructor must be a first statement in Java constructor


I know the answer: "we need rules to prevent shooting into your own foot". Ok, I make millions of programming mistakes every day. For real safety, we need only one simple rule: prohibit all statements and do not use Java at all. If we explain everything by "not shooting your foot", this is reasonable. But there is not much reason is such reason.
When I programmed in Delphi, I always wanted the compiler to check me if I read uninitialized variables. I have discovered myself that is is stupid to read uncertain variables because it leads to unpredictable result and is errorenous obviously. By just looking at the code I could see if there is an error. I wished if compiler could do this job. It is also a reliable signal of programming error if function does not return any value. But I never wanted the compiler to enforce me the super constructor first. Why?
You say that constructors just initialize fields. Super fields are derived; extra fields are introduced. From the goal point of view, it does not matter in which order you initialize the variables. I have studied parallel architectures and can say that all the fields can even be assigned in parallel... What? Do you want to use the unitialized fields? Stupid people always want to take away our freedoms and break the JLS rules the God gives to us! Please, policeman, take away that person!
Where do I say so? I'm just saying only about initializing/assigning, not using the fields. Java compiler already defends me from the mistake of accessing notinitialized. Some cases sneak but this example shows how this stupid rule does not save us from the read-accessing incompletely initialized in construction:
public class BadSuper {
    String field;
    public String toString() {
        return "field = " + field;
    }
    public BadSuper(String val) {
        field = val;

        // yea, superfirst does not protect from accessing
        // inconstructed subclass fields. Subclass constr
        // must be called before super()!
        System.err.println(this);
    }
}
public class BadPost extends BadSuper {

    Object o;
    public BadPost(Object o) {
        super("str");
        this. o = o;
    }

    public String toString() { 
        // superconstructor will boom here, because o is not initialized!
        return super.toString() + ", obj = " + o.toString();
    }

    public static void main(String[] args) {
        new BadSuper("test 1");
        new BadPost(new Object());
    }
}
It shows that actually, subfields have to be inilialized before the supreclass! Meantime, java requirement "defends" us from specializing the class by specializing what the super constructor argument is,
public class MyKryo extends Kryo {

    class MyClassResolver extends DefaultClassResolver {
        public Registration register(Registration registration) {
            System.out.println(MyKryo.this.getDepth());
            return super.register(registration);
        }

    }
    MyKryo() {
        // cannot instantiate MyClassResolver in super
        super(new MyClassResolver(), new MapReferenceResolver());
    }
}
Try to make it compilable. It is always pain. Especially, when you cannot assign the argument later.
Initialization order is not important for initialization in general. I could understand that you should not use super methods before initializing super. But, the requirement for super to be the first statement is different. It only saves you from the code that does useful things simply. I do not see how this adds safety. Actually, safety suffers because we need to use ugly workarounds. Doing post-initialization, outside the constructors also degrades safety (otherwise, why do we need constructors?) and defeats the java final safety reenforcer.
To conclude
  1. Reading not initialized is a bug.
  2. Initialization order is not important from the computer science point of view. Doing initalization or computations in different order is not a bug.
  3. Reenforcing read-access to not initialized is good but compilers fail to detect all such bugs
  4. Making super the first does not solve the problem as it
  5. "Prevents" shooting into right things but not into the foot
  6. It requires to invent workarounds,
    1. where, because of complexity of analysis, it is easier to shoot into the foot
    2. doing post-initialization outside the constructors degrades safety (otherwise, why do we need constructors?)
    3. and that degrade safety by defeating final access modifier
When there was java forum alive, java bigots attecked me for these thoughts. Particularly, they dislaked that fields can be initialized in parallel, saying that natural development ensures correctness. When I replied that you could use an advanced engineering to create a human right away, without "developing" any ape first, and it still be an ape, they stopped to listen me. Cos modern technology cannot afford it. Ok, Take something simpler. How do you produce a Renault? Should you construct an Automobile first? No, you start by producing a Renault right away and, once completed, you'll see that this is an automobile. So, the requirement to produce fields in "natural order" is unnatural. In case of alarmclock or armchair, which are still chair and clock, you may need first develop the base (clock and chair) and then add extra. So, I can have examples where superfields must be initialized first and, oppositely, when they need to be initialized later. The order does not exist in advance. So, the compiler cannot be aware of the proper order. Only programmer/constructor knows is. Compiler should not take more responsibility and enforce the wrong order onto programmer.
Saying that I should not initialize some fields because the others are not initialize is like "you cannot initialize the thing because it is not initialized". This is a kind of argument we have.
So, to conclude once more, the feature that "protects" me from doing things in simple and right way in order to enforce something that does not add noticeably to the bug elimination at that is a strongly negative thing and it pisses me off, altogether with the all the arguments to support it I've seen so far.
It is "a conceptual question about software development" Should there be the requirement to call super() first or not. I do not know. If you do or have an idea, you have place to answer. I think that I have provided enough arguments against this feature. Lets appreciate the ones who benefit form it. Let is just be something more than simple abstract "protection", that can defend every stupid thing. Why do we need it in the language that I am going to develop?

--------------------
I asked this question in http://programmers.stackexchange.com/questions/164250/super-constructor-must-be-a-first-statement-in-java-constructor and figured out that it is not tolerated in programmer community at all, not only among java programmers.

2 comments:

Anonymous said...
This comment has been removed by a blog administrator.
Anonymous said...

Exactly. Every now and then I run into this. It may be that without calling super() Super's fields won't be initialized; the super's constructur can call Sub() by calling an overriden method*) and then Sub is not yet initialized. Only the programmer knows what is best.



*) especially in Java where everything is virtual by default