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?
A simple demonstration:
The output from this program is:
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:
You asked why, and the other answers, imo, don't really say why it's ok to call your super's constructor, but only if it's the very first line. The reason is that you're not really calling the constructor. In C++, the equivalent syntax is
When you see the initializer clause on its own like that, before the open brace, you know it's special. It runs before any of the rest of the constructor runs and in fact before any of the member variables are initialized. It's not that different for Java. There's a way to get some code (other constructors) to run before the constructor really starts, before any members of the subclass are initialized. And that way is to put the "call" (eg
super
) on the very first line. (In a way, thatsuper
orthis
is kind of before the first open brace, even though you type it after, because it will be executed before you get to the point that everything is fully constructed.) Any other code after the open brace (likeint c = a + b;
) makes the compiler say "oh, ok, no other constructors, we can initialize everything then." So it runs off and initializes your super class and your members and whatnot and then starts executing the code after the open brace.If, a few lines later, it meets some code saying "oh yeah when you're constructing this object, here are the parameters I want you to pass along to the constructor for the base class", it's too late and it doesn't make any sense. So you get a compiler error.
Actually you can execute logic with several expessions, you just have to wrap your code in a static function and call it in the super statement.
Using your example:
Tldr:
The other answers have tackled the "why" of the question. I'll provide a hack around this limitation:
The basic idea is to hijack the
super
statement with your embedded statements. This can be done by disguising your statements as expressions.Tsdr:
Consider we want to do
Statement1()
toStatement9()
before we callsuper()
:The compiler will of course reject our code. So instead, we can do this:
The only limitation is that the parent class must have a constructor which takes in at least one argument so that we can sneak in our statement as an expression.
Here is a more elaborate example:
Reworked into:
In fact, compilers could have automated this process for us. They'd just chosen not to.
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.Edit March 2018: In message Records: construction and validation Oracle is suggesting this restriction be removed (but unlike C#,
this
will be definitely unassigned (DU) before constructor chaining).