-5

Why Java compiler (javac) sometimes is stupid, sure i know that Java compiler is just a program, but sometimes is designed as stupid (sometimes not), also i'm a fan of Java :)

class Child extends Base {
    Child() {
        int i = 5; // any statement here.
        super(i);
    }
}

class Base{
    Base(int i) {
    }
}

here compiler, claims that the first statements should be a call to the constructor of the inherited class, but if we wrap this first statement inside a static method, it works fine!!!!!!

class Child extends Base {
    Child() {
        super(foo()); // works fine!!!!
    }

    static int foo(){
        return 5;
    }
}

This works fine!!!!!!!, another killer example :

    Child() {
        try{
            super(5);
        }catch(Exception e){}
    }

try catch is language feature!!!

i know that the compiler oblige us to call the super type constructor, because it should initialise the inherited object before the self object (Generaly Java inheritence is released by object chaining), but i think the compiler should be a little smart, it should let us manipulate the code while we're not touching the object before calling its super constructor, so that MUST work :

    Child() {
        int i = 5; // this MUST BE acceptable since we didn't touch
                   // any current object or inherited field or we didn't
                   // call any method on it.
        super(i);
    }

but this shouldn't work :

class Child extends Base {
    private int i;
    Child() {
        i = 6; // this shouldn't work (its clear why).
        super();
    }
}

I just wanted to understand why this is not implemented especially when i see Java can catch unreachable code (a smart feature)???, so for more than 20 years, Java doesn't provide such a BASIC feature, because usually this one sometimes makes code more ugly, sometimes we have to make stupid static methods to avoid this, other time, we just call the super constructor (for javac shut up) then we reinitialize the inherited fields!!!!!!!!

Althought, i don't think, this is a problem of the JVM and bytecode, i think this is can acheive only in javac :)

I really love Java, but this one makes me so angry, i forget to suggest this for the next release (Java 9), i hope it will be included in Java 10, we wait 3 years more, better than not having it at all :'(

La VloZ Merrill
  • 173
  • 1
  • 10

4 Answers4

2

Both answers are totally correct. I am afraid the OP wants to know the reason behind this behavior.

  • each object does have a constructor
  • each object inherits from Object - so everything is having a parent object
  • at each constructor, the inherited parent is called - implicitly (if you don't call super) or explicitly - if you do call super
  • before you can do anything with your object, it has to be ensured that the inherited object is getting properly initialized.

This is the reason why call to super() has to be the very first in your constructor, and you can not do any operations before calling super().

int i = 5; 

does not work, since you could craft a more tricky initialization:

int i=someMethodCall();

which clearly might use any non-initialized inherited field if it would be allowed.

static int foo(){
        return 5;
    }

works, since it is a static method - which does not depend on the fields of the object instance (and can not see the fields either) - so you can call this before super();

 Child() {
        try{
            super(5);
        }catch(Exception e){}
    }

does not work, since it is not guaranteed that super() will be called any more. Try..catch would allow super(5) to throw an exception (and not initialize inherited fields), while catching and ignoring this exception would yield a Child object which inherited fields are not initialized at all.

Although the behavior might sound stupid - actually it totally makes sense, no?

Gee Bee
  • 1,794
  • 15
  • 17
  • 1
    I think OP was arguing that the Java spec and compiler could be modified to identify if an operation was guaranteed to be "safe" to perform before calling the constructor for the superclass. This answer illuminates the idea that the current requirement is actually a simpler design. We should be careful not to conflate *simplicity* with *stupidity*. Sometimes *simpler* is *smarter*. +10 – spencer7593 Apr 19 '16 at 01:42
  • Agreed. I found close to impossible to analyze if an operation is safe or not before calling the super constructor. If there is any method call, that can potentially *become* unsafe since a descendant class might override and may perform something unsafe. Therefore anything more complicated than an assignment is impossible to check or enforce at compile time of the class (even if the class looks good, a descendant can make it behave bad). – Gee Bee Apr 19 '16 at 12:45
2

I think this may be primarily opinion based.

The way I see it, keeping things simple is sometimes the smarter way to design.

To have the compiler accept some code that the compiler currently doesn't accept, that would (at a minimum) require more complexity. The compiler would have the additional burden of determining which constructs could be allowed, and which constructs wouldn't. The compiler is plenty complex enough. Why add more unnecessary complexity. Especially if the additional complexity doesn't solve, or help us solve, a particular problem.

A requirement that the constructor in the superclass runs before any code in the constructor of the subclass keeps things simpler.

Declaring a static method, as shown in an OP example, doesn't violate the rule about running the constructor in the superclass. A static method is never instantiated. It's part of the class definition, it's not part of an instance of a class.

I think making the compiler smarter (to handle Java code proposed by OP) would not be a smart decision at all.

Q: What real problem does the proposed change to the compiler solve?

Q: And what problems would it potentially create?

Sometimes the choice to avoid additional complexity (and potentially creating more problems) is the smarter choice.

spencer7593
  • 106,611
  • 15
  • 112
  • 140
  • i prefer, your answer, first, because you didn't re-explained what i know, and i really like your answer, making things simpler, i just wondered about that, because 20 years passed and Java designers doesn't tried to solve it, from 90's years, they say its complex task, even today :) – La VloZ Merrill Apr 19 '16 at 07:32
  • @LaVloZMerril: I think the issue is that the Java design team doesn't see this as being a problem, at least, not a problem that needs to be solved. Enforcing one really simple rule: "call the constructor in the superclass first" was their *solution* to a much bigger problem. I believe you are right, there are likely some special cases where a violation of that rule wouldn't cause a problem. But the rule is the rule, and its enforced, with no exceptions for any special cases. – spencer7593 Apr 19 '16 at 19:21
0

Maybe this will give a bit of an insight. Regardless of what you do, the constructor of the super class will always be invoked first.

class Parent {
    Parent(int x) {
        System.out.println("Parent");
    }
}

class Child extends Parent {
    Child () {
        super(foo());
        System.out.println("Child");
    }

    public static void main(String[] args) {
        Child a = new Child();     // prints Parent\nChild
        Parent b = new Child();    // prints Parent\nChild
    }

    static int foo() {
        return 5;
    }
}

As you can see, regardless of what you do, the Parent constructor is always getting called first. This ensures that if you call any methods on the Parent class in your constructor, the Parent class has already been set up correctly. The fact that this property is strictly enforced is shown by the compiler error if I try to move the System.out.println("Child") line before the call to super().

From [JLS 8.8.7.1],

Let C be the class being instantiated, and let S be the direct superclass of C.

It is a compile-time error if S is not accessible (§6.6).

If a superclass constructor invocation statement is qualified, then:

If S is not an inner class, or if the declaration of S occurs in a static context, then a compile-time error occurs.

Otherwise, let p be the Primary expression immediately preceding ".super". Let O be the innermost lexically enclosing class of S.

It is a compile-time error if the type of p is not O or a subclass of O, or if the type of p is not accessible (§6.6).

If a superclass constructor invocation statement is unqualified, and if S is an inner member class, then it is a compile-time error if S is not a member of a lexically enclosing class of C by declaration or inheritance.

Debosmit Ray
  • 5,228
  • 2
  • 27
  • 43
  • Everything before your edit was already stated in their question. They are already aware of that behavior. As for the JLS link, you've copy and pasted. Is your answer that it's like this because the JLS says so or do you have any other comment to make? – Savior Apr 19 '16 at 01:03
  • @Pillar From OP, `i know that the compiler oblige us to call the super type constructor, because it should initialise the inherited object before the self object.......I just wanted to understand why this is not implemented especially when i see Java can catch unreachable code`. From answer, `This ensures that if you call any methods on the Parent class in your constructor, the Parent class has already been set up correctly.` – Debosmit Ray Apr 19 '16 at 01:07
  • @Pillar I haven't copy-pasted the spec from the JLS, I have 'quoted' it. – Debosmit Ray Apr 19 '16 at 01:07
0

If a constructor body does not begin with an explicit constructor invocation and the constructor being declared is not part of the primordial class Object, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its direct superclass that takes no arguments.

From the Java specs, here.

They are actively enforcing this.

Here are the parse rules:

ConstructorBody:
  { [ExplicitConstructorInvocation] [BlockStatements] }

ExplicitConstructorInvocation:
  [TypeArguments] this ( [ArgumentList] ) ; 
  [TypeArguments] super ( [ArgumentList] ) ; 
  ExpressionName . [TypeArguments] super ( [ArgumentList] ) ; 
  Primary . [TypeArguments] super ( [ArgumentList] ) ;

TypeArguments:
  < TypeArgumentList >
ArgumentList:
  Expression {, Expression}
Laurel
  • 5,965
  • 14
  • 31
  • 57