views:

1892

answers:

5

Java requires that if you call this() or super() in a constructor, it must be the first statement. Why?

For example:

public class MyClass {
 public MyClass(int x) {}
}

public class MySubClass extends MyClass {
 public MySubClass(int a, int b) {
  int c = a + b;
  super(c);  // COMPILE ERROR
 }
}

The Sun compiler says "call to super must be first statement in constructor". The Eclipse compiler says "Constructor call must be the first statement in a constructor".

However, you can get around this by re-arranging the code a little bit:

public class MySubClass extends MyClass {
 public MySubClass(int a, int b) {
  super(a + b);  // OK
 }
}

Here is another example:

public class MyClass {
 public MyClass(List list) {}
}

public class MySubClassA extends MyClass {
 public MySubClassA(Object item) {
  // Create a list that contains the item, and pass the list to super
  List list = new ArrayList();
  list.add(item);
  super(list);  // COMPILE ERROR
 }
}

public class MySubClassB extends MyClass {
 public MySubClassB(Object item) {
  // Create a list that contains the item, and pass the list to super
  super(Arrays.asList(new Object[] { item }));  // OK
 }
}

So, it is not stopping you from executing logic before the call to super. It is just stopping you from executing logic that you can't fit into a single expression.

There are similar rules for calling this(). The compiler says "call to this must be first statement in constructor".

Why does the compiler have these restrictions? Can you give a code example where, if the compiler did not have this restriction, something bad would happen?

+7  A: 

The parent class' constructor needs to be called before the subclass' constructor. This will ensure that if you call any methods on the parent class in your constructor, the parent class has already been set up correctly.

What you are trying to do, pass args to the super constructor is perfectly legal, you just need to construct those args inline as you are doing, or pass them in to your constructor and then pass them to super:

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                super(myArray);
        }
}

If the compiler did not enforce this you could do this:

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                someMethodOnSuper(); //ERROR super not yet constructed
                super(myArray);
        }
}

In cases where a parent class has a default constructor the call to super is inserted for you automatically by the compiler. Since every class in Java inherits from Object, objects constructor must be called somehow and it must be executed first. The automatic insertion of super() by the compiler allows this. Enforcing super to appear first, enforces that constructor bodies are executed in the correct order which would be: Object -> Parent -> Child -> ChildOfChild -> SoOnSoForth

anio
agreed. You can't do anything with the object until its parent is constructed. You can work with other stuff, but not that object
CrazyJugglerDrummer
what about anonymous initializer blocks? check my answer.
Savvas Dalkitsis
@Savvas The anonymous intializer does not violate the order of constructor calls, so it is okay. The parent is constructed first, and then the anon initializer code is executed, so it is safe. The constructor bodies must be executed in the following order: Object constructor body -> Parent constructor body -> Child constructor bodyI believe it is to enforce this ordering that the call to super must be first, that way Objects body will execute first since it has no super and then it will unwind and execute the other constructor bodies.
anio
I think I disagree, for two reasons... (1) Checking that super is the first statement is not sufficient to prevent that problem. For example, you could put "super(someMethodInSuper());" in your constructor. This attempts to access a method in the superclass before it is constructed, even though super is the first statement. (2) The compiler appears to implement a different check which is, by itself, sufficient to prevent this problem. The message is "cannot reference xxx before supertype constructor has been called". Therefore, checking that super is the first statement is not necessary.
Joe Daley
@Joe You are correct, placing super() as the first statement does not prevent calling methods on the parent before it is called. As you mentioned that is a separate check. However it does enforce the order in which the bodies of the constructors are executed? Agreed? I believe that this is the reason for making the call to super() the first statement.
anio
+3  A: 

I am fairly sure (those familiar with the Java Specification chime in) that it is to prevent you from (a) being allowed to use a partially-constructed object, and (b), forcing the parent class's constructor to construct on a "fresh" object.

Some examples of a "bad" thing would be:

class Thing
{
    final int x;
    Thing(int x) { this.x = x; }
}

class Bad1 extends Thing
{
    final int z;
    Bad1(int x, int y)
    {
        this.z = this.x + this.y; // WHOOPS! x hasn't been set yet
        super(x);
    }        
}

class Bad2 extends Thing
{
    final int y;
    Bad2(int x, int y)
    {
        this.x = 33;
        this.y = y; 
        super(x); // WHOOPS! x is supposed to be final
    }        
}
Jason S
Should `Bad1` and `Bad2` extend `Thing` there?
Michael Myers
Ah, thanks. duh.
Jason S
A: 
class A {
  private final int x;
  public A(int x_) { x = x_; }
  public getX() { return x; }
}

class B extends A {
  private int y = getX();
  public B {
    System.out.println("What is this:" + y);
    super(10);
  }
}
caskey
What is this? this doesn't even compile. Does this reply have a point?
Savvas Dalkitsis
Sorry, didn't mean to make you think. It answers the very last question in the OP."Can you give a code example where, if the compiler did not have this restriction, something bad would happen?"
caskey
The point is that it doesn't compile, and it's a good thing that it doesn't.
Michael Myers
You could put that in as a description. And thank you for your kind words...
Savvas Dalkitsis
You're welcome.
caskey
Perhaps then you should say that in the answer along with the code.
MattC
Because the question is in the question and I think it's reasonable to expect the reader has read the question before jumping to the answers.
caskey
self-explanatory code sometimes isn't (especially for interested beginners) -- comments as to what you are saying and why are useful and lack thereof may turn people off (I didn't downvote you but someone did.)
Jason S
A: 

You can use anonymous initializer blocks to initialize fields in the child before calling it's constructor. This example will demonstrate :

public class Test {
    public static void main(String[] args) {
     new Child();
    }
}

class Parent {
    public Parent() {
     System.out.println("In parent");
    }
}

class Child extends Parent {

    {
     System.out.println("In initializer");
    }

    public Child() {
     super();
     System.out.println("In child");
    }
}

This will output :

In parent
In initializer
In child

Savvas Dalkitsis
+2  A: 

Because the JLS says so. Could the JLS be changed in a compatible manner to allow it? Yup. However, it would complicate the language spec, which is already more than complicated enough. It wouldn't be a highly useful thing to do and there are ways around it (call another constructor with the result of a method this(fn()) - the method is called before the other constructor, and hence also the super constructor). So the power to weight ratio of doing the change is unfavourable.

Tom Hawtin - tackline
just to clarify: the fn() you used in your example should be a static method, right?
Jason S
Yup - you can't touch this.
Tom Hawtin - tackline
: - )
Jason S