Why do this() and super() have to be the first statement in a constructor?
Asked Answered
C

22

734

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?

Carlock answered 22/7, 2009 at 21:25 Comment(4)
A good question. I have started a similar in valjok.blogspot.com/2012/09/… and programmers.exchange where I show that there are cases where subfields must be initialized prior to the super(). So that the feature adds to complexity of doing things whereas it is not clear if positive impacts regarding to "code safety" overweight the negative ones. Yes, there are negative consequences of super always first. Surprisingly that nobody mentions this. I think that this is a conceptual thing and must be asked in programmers.exchangeDiscover
The worst part is that this is purely a Java restriction. At the bytecode level there's no such restriction.Bor
Well, it would be impossible to have that restriction at the bytecode level - all the examples in this post would violate such a restriction, even the ones that stuff all the logic into a single expression.Benzvi
Possible duplicate of call to super() must be the first statement in constructor bodyEyehole
T
220

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

Thole answered 22/7, 2009 at 21:27 Comment(11)
agreed. You can't do anything with the object until its parent is constructed. You can work with other stuff, but not that objectPumpkinseed
what about anonymous initializer blocks? check my answer.Turquoise
@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 body I 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.Thole
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.Carlock
@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.Thole
Considering the compiler knows when you're accessing parent methods/fields I don't see why you can't be allowed something along the lines of Constructor(int x) { this.field1 = x; super(); }. Sure, you shouldn't need to in an ideal world where you control the code, but that's not always the case. The reason I looked this up in the first place was because I was annoyed I couldn't use it to get around a flaw in 3rd party code.Elimination
Agreeing with @JoeDaley I think the fact that C# does not have this restriction is enough to suggest that this problem can be solved in less ham-fisted ways.Perverted
@Tom Indeed, but this isn't C#, and we have pretty much established that Sun was kinda dumb when deciding about some constructs. :/Verbid
FYI, very often when it seems that you need to do logic before calling super, you are better off using composition instead of inheritance.Registry
@TomLianza What makes you think C# doesn't have this restriction? The base constructor call always happens before the constructor body is executed and you can only access static members if you nest expressions in the base call.Alis
To my mind this is an issue where the standard prevents a different statement other than the super call to be the first statement in a constructor, while the standard aims to prevent a different call other than the super call to be be the first call during object initialization. These things turn out not to be equivalent.Unguentum
T
113

I've found a way around this by chaining constructors and static methods. What I wanted to do looked something like this:

public class Foo extends Baz {
  private final Bar myBar;

  public Foo(String arg1, String arg2) {
    // ...
    // ... Some other stuff needed to construct a 'Bar'...
    // ...
    final Bar b = new Bar(arg1, arg2);
    super(b.baz()):
    myBar = b;
  }
}

So basically construct an object based on constructor parameters, store the object in a member, and also pass the result of a method on that object into super's constructor. Making the member final was also reasonably important as the nature of the class is that it's immutable. Note that as it happens, constructing Bar actually takes a few intermediate objects, so it's not reducible to a one-liner in my actual use case.

I ended up making it work something like this:

public class Foo extends Baz {
  private final Bar myBar;

  private static Bar makeBar(String arg1,  String arg2) {
    // My more complicated setup routine to actually make 'Bar' goes here...
    return new Bar(arg1, arg2);
  }

  public Foo(String arg1, String arg2) {
    this(makeBar(arg1, arg2));
  }

  private Foo(Bar bar) {
    super(bar.baz());
    myBar = bar;
  }
}

Legal code, and it accomplishes the task of executing multiple statements before calling the super constructor.

Theophrastus answered 15/7, 2011 at 13:47 Comment(6)
This technique can be extended. If super takes many parameters or you need to set other fields at the same time, create a static inner class to hold all variables, and use it to pass data from the static method to the single-arg constructor.Registry
FYI, very often when it seems that you need to do logic before calling super, you are better off using composition instead of inheritance.Registry
it took me a little while before I've got to understand your concept. So basicaly you create static method and put it into constructor.Vevina
@AleksandrDubinsky Can you please elaborate (provide sample code) showing how to use a static inner class to set multiple super parameters at the same time? Perhaps this is discussed in greater detail in another post you can link to?Deaconry
+1, this addresses the issue created by Java's restriction. But it doesn't answer OP's question, which is why Java do compilers have these restrictions?Isom
@AleksandrDubinsky do agree on using composition, but again if i do want to override few methods for some inherited classes, composition wont help right, and you simply need to define a dedicated method if using compositionSturgill
C
60

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 static method or lambda expression 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.

Note that this rule alone does not prevent use of fields before the super class has completed construction.

Consider these illegal examples.

super(this.x = 5);

super(this.fn());

super(fn());

super(x);

super(this instanceof SubClass);
// this.getClass() would be /really/ useful sometimes.

This example is legal, but "wrong".

class MyBase {
    MyBase() {
        fn();
    }
    abstract void fn();
}
class MyDerived extends MyBase {
    void fn() {
       // ???
    }
}

In the above example, if MyDerived.fn required arguments from the MyDerived constructor they would need to be sleazed through with a ThreadLocal. ;(

Incidentally, since Java 1.4, the synthetic field that contains the outer this is assigned before inner classes super constructor is called. This caused peculiar NullPointerException events in code compiled to target earlier versions.

Note also, in the presence of unsafe publication, construction can be viewed reordered by other threads, unless precautions are made.

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).

Historically, this() or super() must be first in a constructor. This restriction was never popular, and perceived as arbitrary. There were a number of subtle reasons, including the verification of invokespecial, that contributed to this restriction. Over the years, we've addressed these at the VM level, to the point where it becomes practical to consider lifting this restriction, not just for records, but for all constructors.

Chancy answered 22/7, 2009 at 21:38 Comment(10)
just to clarify: the fn() you used in your example should be a static method, right?Uxorious
+1 for mentioning that this is purely a JLS restriction. At the bytecode level, you can do other stuff before calling a constructor.Bor
Wait, how could this complicate the language spec? And the moment the spec says that the first statement may be a constructor, all the other statements cannot be constructors. When you remove the restriction, to spec will be something like "you just have statements inside". How is this more complicated?Counterpoise
@Counterpoise you get the answer, when you compare it with the relevant JVM specification parts. As said by Antimony, this restriction does not exist on the byte code level, but, of course, the requirement to invoke a super constructor and not to use the object under construction before the super constructor has been called, still exists. So the definition of correct code and how to verify its correctness fills entire pages. Offering the same freedom in the JLS requires a similar complexity, as the JLS can’t allow things that are illegal on the byte code level.Great
@Great In the interests of balance, I note that there is code that can be executed in a constructor before the call to super()/this(). This code is the arguments if super()/this() has parameters. However, there's much more nonsense you could get up to with whole statements.Chancy
@TomHawtin-tackline this distinction between expressions and statements is not that helpful here. It made the original design of the Java language easier, I’ve seen grammars considering the this() or super() call as part of the constructor grammar. But today, we can stuff arbitrary statements into a lambda expression and pass it as argument to another this or super constructor, which executes it immediately…Great
@Great But you can't for instance use if to conditionally execute this(). Or for more fun, surround with a try.Chancy
@TomHawtin-tackline exactly. That’s why I said, carrying the complexity of the JVMS, which allows it with certain restrictions, into the JLS would not be that simple as Uko thought.Great
I've always found the answer "because it was spec’d that way" to the question "why is X like this?" a little more than unsatisfying. Usually when people ask why X is the way it is, they're really asking how the decision was made to make X the way it is.Petronius
@Petronius It could have gone either way. That's the point I want to make straightaway.Chancy
J
17

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 either

  • an explicit invocation of another constructor of the same class (by using the keyword "this"); or
  • an explicit invocation 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 ...

Jerk answered 29/9, 2013 at 9:31 Comment(3)
I know that constructors aren't processed as function calls, but I would think that interpreting each super constructor call as this = [new object] and requiring that this be in defined before it is used as and before a constructor returns would be semantically sufficient to achieve the stated objectives. The inability to wrap the parent constructor calls in a try-catch-rethrow or try/finally block makes it impossible to have a subclass constructor promise not to throw something the superclass constructor might, even if the subclass would be able to guarantee...Pleinair
...that the exception couldn't occur. It also greatly increases the difficulty of safely chaining constructors which need to acquire resources and pass them to the parent constructor (the child constructor needs to invoked by a factory method that creates a container for the resources, invokes the constructor within a try block, and discards any resources in the container if the constructor fails.Pleinair
Technically it's not the first line, but rather the first executable statement in the constructor. It's perfectly legal to have comments before the explicit constructor invocations.Karakorum
U
13

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
    }        
}
Uxorious answered 22/7, 2009 at 21:28 Comment(3)
Should Bad1 and Bad2 extend Thing there?Fuddyduddy
I disagree with Bad2 as x gets declared in Thing and simply must not be set anywhere else. As for Bad1, you're surely right, but a similar thing may happen when the super constructor invokes a method overridden in the subclass which accesses a (not yet initialized) variable of the subclass. So the restriction helps to prevent one part of the problem... which is IMHO not worth it.Ladyfinger
@Ladyfinger the difference is, the author of the superclass constructor has the responsibility regarding invoking overridable methods. So it’s possible to design the superclass in a way that it always has a consistent state, which would not be possible, if subclasses were allowed to use the object before the superclass constructor has been called.Great
K
9

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

MySubClass: MyClass {

public:

 MySubClass(int a, int b): MyClass(a+b)
 {
 }

};

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, that super or this 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 (like int 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.

Kimmi answered 14/7, 2011 at 17:56 Comment(4)
1. If java designers wanted superconstructor implicit they could just do that and, more importantly, this does not explain why implicit superconstructor is very useful. 2. IMO, it is your comment that it does not make any sense does not make any sense. I remember I needed that. Can you prove that I did something meaningless?Discover
imagine you need to enter a room. The door is locked, so you smash a window, reach in and let yourself in. Inside, halfway across the room, you find a note with a key for you to use on the way in. But you're already in. Similarly if the compiler is halfway through executing a constructor and it comes across "here's what to do with those parameters before you run the constructor" what is it supposed to do?Kimmi
If it is stupid thing in reality then it is a wrong analogy. If I am in a position to decide, which way to go, I am not in the half way. It is the rule that the supercall must be the first in constructor that prvoces us to break the window (see a lot of examples of warkingaround in the question and answers) instead of using the door. So, you put everything upside down when trying to argue for this rule. The rule must be wrong, therefore.Discover
-1 This does not reflect how the code is actually compiled in Java, the constraints on it, or the actual reason for designing Java the way it is.Bor
P
6

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.

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:

public class MySubClassC extends MyClass {
    public MySubClassC(Object item) {
        // Create a list that contains the item, and pass the list to super
        super(createList(item));  // OK
    }

    private static List createList(item) {
        List list = new ArrayList();
        list.add(item);
        return list;
    }
}
Predatory answered 18/6, 2014 at 9:0 Comment(2)
This works only if the super class constructor expects a single, non-void argumentPoesy
instead of choosing to make super()/this() as first statement, language designers could have chosen to enforce no instance method or inherited method calls can be done before calling super()/this(). So maybe OP is inquiring why it was not done this way.Tulle
F
5

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.:

class Sup {
    public Sup(final int x_) { 
        //cheap constructor 
    }
    public Sup(final Sup sup_) { 
        //expensive copy constructor 
    }
}

class Sub extends Sup {
    private int x;
    public Sub(final Sub aSub) {
        /* for aSub with aSub.x == 0, 
         * the expensive copy constructor is unnecessary:
         */

         /* if (aSub.x == 0) { 
          *    super(0);
          * } else {
          *    super(aSub);
          * } 
          * above gives error since if-construct before super() is not allowed.
          */

        /* super((aSub.x == 0) ? 0 : aSub); 
         * above gives error since the ?-operator's type is Object
         */

        super(aSub); // much slower :(  

        // further initialization of aSub
    }
}

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.

Frick answered 14/7, 2011 at 17:38 Comment(2)
I think the downvote is because you're not answering the question, but making comments on the issue. Would be OK in a forum, but SO/SE is not one :)Karakorum
Excellent example of the ways the ?: construct's type can surprise you. I was thinking as I read, "It's not impossible --- just use a ternary oper... Oh.".Pomposity
C
3

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() or this() call to move around, there are more variations to check for. For example if you move the super() or this() call into a conditional if() it might have to be smart enough to insert an implicit super() into the else. It might need to know how to report an error if you call super() twice, or use super() and this() together. It might need to disallow method calls on the receiver until super() or this() is called and figuring out when that is becomes complicated.

Making everyone do this extra work probably seemed like a greater cost than benefit.

Contraband answered 21/4, 2014 at 19:4 Comment(1)
Writing a sane grammar for the feature would in itself be quite hard - such a grammar would match a statement tree where at most one leaf node is an explicit super-constructor call. I can think of a way to write it, but my approach would be quite insane.Schnurr
S
3

I found a woraround.

This won't compile :

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

This works :

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        this(a + b);
        doSomething2(a);
        doSomething3(b);
    }

    private MySubClass(int c) {
        super(c);
        doSomething(c);
    }
}
Simpleminded answered 30/10, 2015 at 14:26 Comment(2)
The question is not about a workaround. In fact a work around can be found in the question itself.Devault
This is not a workaround. You still can't write multi lines of code.Farica
M
3

It makes sense that constructors complete their execution in order of derivation. Because a superclass has no knowledge of any subclass, any initialization it needs to perform is separate from and possibly prerequisite to any initialization performed by the subclass. Therefore, it must complete its execution first.

A simple demonstration:

class A {
    A() {
        System.out.println("Inside A's constructor.");
    }
}

class B extends A {
    B() {
        System.out.println("Inside B's constructor.");
    }
}

class C extends B {
    C() {
        System.out.println("Inside C's constructor.");
    }
}

class CallingCons {
    public static void main(String args[]) {
        C c = new C();
    }
}

The output from this program is:

Inside A's constructor
Inside B's constructor
Inside C's constructor
Modulate answered 5/6, 2017 at 13:53 Comment(1)
In this example, there is default constructor in each class and so no emergency need to call super(...,...) method in the subclassMapp
E
3

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

class Good {
    int essential1;
    int essential2;

    Good(int n) {
        if (n > 100)
            throw new IllegalArgumentException("n is too large!");
        essential1 = 1 / n;
        essential2 = n + 2;
    }
}

class Bad extends Good {
    Bad(int n) {
        try {
            super(n);
        } catch (Exception e) {
            // Exception is ignored
        }
    }

    public static void main(String[] args) {
        Bad b = new Bad(0);
//        b = new Bad(101);
        System.out.println(b.essential1 + b.essential2);
    }
}

An exception during construction almost always indicates that the object being constructed could not be properly initialized, now is in a bad state, unusable, and must be garbage collected. However, a constructor of a subclass has got the ability to ignore an exception occurred in one of its superclasses and to return a partially initialized object. In the above example, if the argument given to new Bad() is either 0 or greater than 100, then neither essential1 nor essential2 are properly initialized.

You may say that ignoring exceptions is always a bad idea. OK, here's another example:

class Bad extends Good {
    Bad(int n) {
        for (int i = 0; i < n; i++)
            super(i);
    }
}

Funny, isn't it? How many objects are we creating in this example? One? Two? Or maybe nothing...

Allowing to call super() or this() in the middle of a constructor would open a Pandora's box of heinous constructors.


On the other hand, I understand a frequent need to include some static part before a call to super() or this(). This might be any code not relying on this reference (which, in fact, already exists at the very beginning of a constructor, but cannot be used orderly until super() or this() returns) and needed to make such call. In addition, like in any method, there's a chance that some local variables created before the call to super() or this() will be needed after it.

In such cases, you have the following opportunities:

  1. Use the pattern presented at this answer, which allows to circumvent the restriction.
  2. Wait for the Java team to allow pre-super() and pre-this() code. It may be done by imposing a restriction on where super() or this() may occur in a constructor. Actually, even today's compiler is able to distinguish good and bad (or potentially bad) cases with the degree enough to securely allow static code addition at the beginning of a constructor. Indeed, assume that super() and this() return this reference and, in turn, your constructor has

return this;

at the end. As well as the compiler rejects the code

public int get() {
    int x;
    for (int i = 0; i < 10; i++)
        x = i;
    return x;
}

public int get(int y) {
    int x;
    if (y > 0)
        x = y;
    return x;
}

public int get(boolean b) {
    int x;
    try {
        x = 1;
    } catch (Exception e) {
    }
    return x;
}

with the error "variable x might not have been initialized", it could do so on this variable, making its checks on it just like on any other local variable. The only difference is this cannot be assigned by any means other than super() or this() call (and, as usual, if there is no such call at a constructor, super() is implicitly inserted by compiler in the beginning) and might not be assigned twice. In case of any doubt (like in the first get(), where x is actually always assigned), the compiler could return an error. That would be better than simply return error on any constructor where there is something except a comment before super() or this().

Expiate answered 30/7, 2018 at 7:52 Comment(1)
this is late, but you could also use the factory pattern. Make the constructors private. Make static methods associated with the constructors. Let us call the class Foo, 2 constructors, Foo() and Foo(int i), and static methods that constructs it, createFoo() and createFoo(int i). Then replace this() with Foo.createFoo(). Therefore, you can do stuff in createFoo(int i) and lastly do Foo.createFoo. Or any other order. It is sort of like a factory design pattern, but not.Trihedral
P
3

Java language architect Brian Goetz commented on this in the Amber Expert Group mailing list:

Historically, this() or super() must be first in a constructor. This restriction was never popular, and perceived as arbitrary. There were a number of subtle reasons, including the verification of invokespecial, that contributed to this restriction. Over the years, we've addressed these at the VM level, to the point where it becomes practical to consider lifting this restriction, not just for records, but for all constructors.

Polygon answered 22/2, 2022 at 3:59 Comment(1)
Quotes should be attributed. I've edited you answer to add a proper attribution.Hyperbolize
T
2

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

Turquoise answered 22/7, 2009 at 21:34 Comment(3)
But this adds nothing over just adding the System.out.println("In initializer") as the first line after "super()", does it? What would be useful would be a way to execute code before the parent is constructed.Alida
Indeed. If you will try to add something, you'll need so save the computed state somewhere. Even if compiler allows you to, what will be the temporary storage? Allocate one more field just for initialization? But this is waste of memory.Discover
This is incorrect. Instance initializers are inserted after the parent constructor call returns.Bor
M
1

I 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:

public T run(Object... args);

And if I need to do something before passing it to the constructor I just do this:

super(new InfoRunnable<ThingToPass>() {
    public ThingToPass run(Object... args) {
        /* do your things here */
    }
}.run(/* args here */));
Merill answered 22/7, 2016 at 17:22 Comment(0)
K
1

Actually, super() is the first statement of a constructor because to make sure its superclass is fully-formed before the subclass being constructed. Even if you don't have super() in your first statement, the compiler will add it for you!

Konstanze answered 30/12, 2017 at 2:40 Comment(1)
The compiler implicitly adds super() only if you have a default contructor in your base class. It cannot summon parameter values.Imperfect
A
1

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.

Acetaldehyde answered 4/10, 2018 at 14:3 Comment(0)
K
1

The question of why Java does this has already been answered, but since I stumbled upon this question hoping to find a better alternative to the one-liner, I'll hereby share my work-around:

public class SomethingComplicated extends SomethingComplicatedParent {

    private interface Lambda<T> {
        public T run();
    }

    public SomethingComplicated(Settings settings) {
        super(((Lambda<Settings>) () -> {

            // My modification code,
            settings.setting1 = settings.setting2;
            return settings;
        }).run());
    }
}

Calling a static function should perform better, but I would use this if I insist on having the code "inside" the constructor, or if I have to alter multiple parameters and find defining many static methods bad for readability.

Kipkipling answered 29/9, 2020 at 1:0 Comment(0)
R
0

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() to Statement9() before we call super():

public class Child extends Parent {
    public Child(T1 _1, T2 _2, T3 _3) {
        Statement_1();
        Statement_2();
        Statement_3(); // and etc...
        Statement_9();
        super(_1, _2, _3); // compiler rejects because this is not the first line
    }
}

The compiler will of course reject our code. So instead, we can do this:

// This compiles fine:

public class Child extends Parent {
    public Child(T1 _1, T2 _2, T3 _3) {
        super(F(_1), _2, _3);
    }

    public static T1 F(T1 _1) {
        Statement_1();
        Statement_2();
        Statement_3(); // and etc...
        Statement_9();
        return _1;
    }
}

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:

public class Child extends Parent {
    public Child(int i, String s, T1 t1) {
        i = i * 10 - 123;
        if (s.length() > i) {
            s = "This is substr s: " + s.substring(0, 5);
        } else {
            s = "Asdfg";
        }
        t1.Set(i);
        T2 t2 = t1.Get();
        t2.F();
        Object obj = Static_Class.A_Static_Method(i, s, t1);
        super(obj, i, "some argument", s, t1, t2); // compiler rejects because this is not the first line
    }
}

Reworked into:

// This compiles fine:

public class Child extends Parent {
    public Child(int i, String s, T1 t1) {
        super(Arg1(i, s, t1), Arg2(i), "some argument", Arg4(i, s), t1, Arg6(i, t1));
    }

    private static Object Arg1(int i, String s, T1 t1) {
        i = Arg2(i);
        s = Arg4(s);
        return Static_Class.A_Static_Method(i, s, t1);
    }

    private static int Arg2(int i) {
        i = i * 10 - 123;
        return i;
    }

    private static String Arg4(int i, String s) {
        i = Arg2(i);
        if (s.length() > i) {
            s = "This is sub s: " + s.substring(0, 5);
        } else {
            s = "Asdfg";
        }
        return s;
    }

    private static T2 Arg6(int i, T1 t1) {
        i = Arg2(i);
        t1.Set(i);
        T2 t2 = t1.Get();
        t2.F();
        return t2;
    }
}

In fact, compilers could have automated this process for us. They'd just chosen not to.

Repudiate answered 24/8, 2014 at 19:42 Comment(2)
In the second code block, super(F(), _2, _3); should be super(F(_1), _2, _3);Karakorum
"parent class must have a constructor which takes in at least one argument" is not true--simply make another constructor in your own class that takes an argument.Registry
A
0

Before you can construct child object your parent object has to be created. As you know when you write class like this:

public MyClass {
        public MyClass(String someArg) {
                System.out.println(someArg);
        }
}

it turns to the next (extend and super are just hidden):

public MyClass extends Object{
        public MyClass(String someArg) {
                super();
                System.out.println(someArg);
        }
}

First we create an Object and then extend this object to MyClass. We can not create MyClass before the Object. The simple rule is that parent's constructor has to be called before child constructor. But we know that classes can have more that one constructor. Java allow us to choose a constructor which will be called (either it will be super() or super(yourArgs...)). So, when you write super(yourArgs...) you redefine constructor which will be called to create a parent object. You can't execute other methods before super() because the object doesn't exist yet (but after super() an object will be created and you will be able to do anything you want).

So why then we cannot execute this() after any method? As you know this() is the constructor of the current class. Also we can have different number of constructors in our class and call them like this() or this(yourArgs...). As I said every constructor has hidden method super(). When we write our custom super(yourArgs...) we remove super() with super(yourArgs...). Also when we define this() or this(yourArgs...) we also remove our super() in current constructor because if super() were with this() in the same method, it would create more then one parent object. That is why the same rules imposed for this() method. It just retransmits parent object creation to another child constructor and that constructor calls super() constructor for parent creation. So, the code will be like this in fact:

public MyClass extends Object{
        public MyClass(int a) {
                super();
                System.out.println(a);
        }
        public MyClass(int a, int b) {
                this(a);
                System.out.println(b);
        }
}

As others say you can execute code like this:

this(a+b);

also you can execute code like this:

public MyClass(int a, SomeObject someObject) {
    this(someObject.add(a+5));
}

But you can't execute code like this because your method doesn't exists yet:

public MyClass extends Object{
    public MyClass(int a) {

    }
    public MyClass(int a, int b) {
        this(add(a, b));
    }
    public int add(int a, int b){
        return a+b;
    }
}

Also you are obliged to have super() constructor in your chain of this() methods. You can't have an object creation like this:

public MyClass{
        public MyClass(int a) {
                this(a, 5);
        }
        public MyClass(int a, int b) {
                this(a);
        }
}
Abingdon answered 29/11, 2016 at 13:29 Comment(0)
S
0
class C
{
    int y,z;

    C()
    {
        y=10;
    }

    C(int x)
    {
        C();
        z=x+y;
        System.out.println(z);
    }
}

class A
{
    public static void main(String a[])
    {
        new C(10);
    }
}

See the example if we are calling the constructor C(int x) then value of z is depend on y if we do not call C() in the first line then it will be the problem for z. z would not be able to get correct value.

Sphacelus answered 2/3, 2017 at 6:0 Comment(0)
C
0

The main goal of adding the super() in the sub-class constructors is that the main job of the compiler is to make a direct or indirect connection of all the classes with the Object class that's why the compiler checks if we have provided the super(parameterized) then compiler doesn't take any responsibility. so that all the instance member gets initialized from Object to the sub - classes.

Conure answered 7/11, 2020 at 10:24 Comment(1)
What "responsibility"? As if you could sue the compiler... :)Imperfect

© 2022 - 2024 — McMap. All rights reserved.