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?
That's because your constructor depends on other constructors. To your constructor work correctly its necessary to other constructor works correctly which is dependent. That's why its necessary to check dependent constructors first which called by either this() or super() in your constructor. If other constructors which called by either this() or super() have a problem so whats point execute other statements because all will fail if called constructor fails.
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
:If the compiler did not enforce this you could do this:
In cases where a
parent
class has a default constructor the call to super is inserted for you automatically by thecompiler
. Since every class in Java inherits fromObject
, 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 -> SoOnSoForthI know I am a little late to the party, but I've used this trick a couple of times (and I know it's a bit unusual):
I create an generic interface
InfoRunnable<T>
with one method:And if I need to do something before passing it to the constructor I just do this:
Simply because this is the inheritance philosophy. And according to the Java language specification, this is how the constructor's body is defined:
ConstructorBody: { ExplicitConstructorInvocationopt BlockStatementsopt }
The first statement of a constructor body may be:
-an explicit invocation of another constructor of the same class (by using the keyword "this") OR
-of the direct superclass (by using the keyword "super")
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. And so on.. there will be a whole chain of constructors called all the way back to the constructor of Object; "All Classes in the Java platform are Descendants of Object". This thing is called "Constructor Chaining".
Now why is this?
And the reason why Java defined the ConstructorBody in this way, is that they needed to maintain the hierarchy of the object. Remember the definition of the inheritance; It's extending a class. With that being said, you cannot extend something that doesn't exist. The base (the superclass) needs to be created first, then you can derive it (the subclass). That's why they called them Parent and Child classes; you can't have a child without a parent.
On a technical level, a subclass inherits all the members (fields, methods, nested classes) from its parent. And since Constructors are NOT members (They don't belong to objects. They are responsible of creating objects) so they are NOT inherited by subclasses, but they can be invoked. And since at the time of object creation only ONE constructor is executed. So how do we guarantee the creation of the superclass when you create the subclass object? Thus the concept of "constructor chaining"; so we have the ability to invoke other constructors (i.e. super) from within the current constructor. And Java required this invocation to be the FIRST line in the subclass constructor to maintain the hierarchy and guarantee it. They assume that if you don't explicitly create the parent object FIRST (like if you forgot about it), they will do it implicitly for you.
This check is done during compilation. But I'm not sure what would happen on runtime, what kind of runtime error we would get, IF Java doesn't throw a compile-error when we explicitly try to execute a base constructor from within a subclass's constructor in the middle of its body and not from the very first line ...
I totally agree, the restrictions are too strong. Using a static helper method (as Tom Hawtin - tackline suggested) or shoving all "pre-super() computations" into a single expression in the parameter is not always possible, e.g.:
Using an "object not yet constructed" exception, as Carson Myers suggested, would help, but checking this during each object construction would slow down execution. I would favor a Java compiler that makes a better differentiation (instead of inconsequently forbidding an if-statement but allowing the ?-operator within the parameter), even if this complicates the language spec.
My guess is they did this to make life easier for people writing tools that process Java code, and to some lesser degree also people who are reading Java code.
If you allow the
super()
orthis()
call to move around, there are more variations to check for. For example if you move thesuper()
orthis()
call into a conditionalif()
it might have to be smart enough to insert an implicitsuper()
into theelse
. It might need to know how to report an error if you callsuper()
twice, or usesuper()
andthis()
together. It might need to disallow method calls on the receiver untilsuper()
orthis()
is called and figuring out when that is becomes complicated.Making everyone do this extra work probably seemed like a greater cost than benefit.