13

Let's look at the following code snippet in Java.

package trickyjava;

class A
{
    public A(String s)
    {
        System.out.println(s);
    }
}

final class B extends A
{
    public B()
    {
        super(method());      // Calling the following method first.      
    }

    private static String method()
    {
        return "method invoked";
    }
}

final public class Main
{
    public static void main(String[] args)
    {
        B b = new B();
    }
}

By convention, the super() constructor in Java must be the first statement in the relevant constructor body. In the above code, we are calling the static method in the super() constructor parameter list itself super(method());.


It means that in the call to super in the constructor B(), a method is being called BEFORE the call to super is made! This should be forbidden by the compiler but it works nice. This is somewhat equivalent to the following statements.

String s = method();
super(s);

However, it's illegal causing a compile-time error indicating that "call to super must be first statement in constructor". Why? and why it's equivalent super(method()); is valid and the compiler doesn't complain any more?

Ravi Bhatt
  • 3,147
  • 19
  • 21
Lion
  • 18,729
  • 22
  • 80
  • 110
  • See http://stackoverflow.com/questions/1168345/why-does-this-and-super-have-to-be-the-first-statement-in-a-constructor – wannik Nov 11 '11 at 23:07
  • 4
    `super` IS the first statement in the constructor, despite not being called first. The specification state that `The first statement of a constructor body may be an explicit invocation of another constructor ...`, not that it must be called first. – user85421 Nov 11 '11 at 23:24

6 Answers6

10

The key thing here is the static modifier. Static methods are tied to the class, instance methods (normal methods) are tied to an object (class instance). The constructor initializes an object from a class, therefore the class must already have been fully loaded. It is therefore no problem to call a static method as part of the constructor.

The sequence of events to load a class and create an object is like this:

  1. load class
  2. initialize static variables
  3. create object
  4. initialize object <-- with constructor
  5. object is now ready for use

(simplified*)

By the time the object constructor is called, the static methods and variables are available.

Think of the class and its static members as a blueprint for the objects of that class. You can only create the objects when the blueprint is already there.

The constructor is also called the initializer. If you throw an exception from a constructor and print the stack trace, you'll notice it's called <init> in the stack frame. Instance methods can only be called after an object has been constructed. It is not possible to use an instance method as the parameter for the super(...) call in your constructor.

If you create multiple objects of the same class, steps 1 and 2 happen only once.

(*static initializers and instance initializers left out for clarity)

Barend
  • 17,296
  • 2
  • 61
  • 80
  • 1
    Actually, you could call an instance method of another class, or even this one, if you got the instance from somewhere else (eg, a static method or a public static field, or a parameter to the init method). You just can't use "this". – Hot Licks Nov 12 '11 at 04:53
5

Yep, checking the JVM spec (though admittedly an old one):

In the instance init method, no reference to "this" (including the implicit reference of a return) may occur before a call to either another init method in the same class or an init method in the superclass has occurred.

This is really the only real restriction, so far as I can see.

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
4

The aim of requiring the super constructor to be invoked first is to ensure that the "super object" is fully initialized before it is used (It falls short of actually enforcing this because the super constructor can leak this, but that's another matter).

Calling a non-static method on this would allow the method to see uninitialized fields and is therefore forbidden. A static method can only see these fields if it is passed this as argument. Since accessing this and super is illegal in super constructor invocation expressions, and the call to super happens before the declaration of any variables that might point to this, allowing calls to static methods in super constructor invocation expressions is safe.

It is also useful, because it allows to compute the arguments to the super constructor in an arbitrarily complex manner. If calls to static methods weren't allowed, it would be impossible to use control flow statements in such a computation. Something as simple as:

class Sub extends Super {
    Sub(Integer... ints) {
        super(Arrays.asList(ints));
    }
}

would be impossible.

meriton
  • 68,356
  • 14
  • 108
  • 175
3

This is one situation where the java syntax hides what's really going on, and C# makes it a bit clearer.

In C# your B would look like

class B : A {
    public B() : base(method()) {
    }

    private static String method() {
        return "method invoker";
    }
}

Although the java syntax places super(method) within the constructor it's not really called there: All the parent initialization is run before your subclass constructor. The C# code shows this a little more clearly; by placing super(method()) at the first line of the java constructor you're simply telling java to use the parameterized constructor of the super class rather than the parameterless version; this way you can pass variables to the parent constructor and they'll be used in the initialization of the parent level fields before your child's constructor code runs.

The reason that super(method()) is valid (as the first line in a java constructor) is because method() is being loaded with the static elements--before the non-static ones, including the constructors--which allows it to be called not only before B(), but before A(String) as well. By saying

public B() {
   String s = method();
   super(s);
}

you're telling the java compiler to initialize the super object with the default constructor (because the call to super() isn't the first line) and you're ready to initialize the subclass, but the compiler then becomes confused when it sees that you're trying to initialize with super(String) after super() has already run.

Guildencrantz
  • 1,875
  • 1
  • 16
  • 30
2

A call to super is a must in java to allow the parent to get initalized before anything with child class starts.

In the case above, if java allows String s= method(); before the call to super, it opens up flood gate of things that can be done before a call to super. that would risk so many things, essentially that allows a half baked class to be used. Which is rightly not allowed. It would allow things like object state (some of which may belong to the parent) being modified before it was properly created.

In case of super(method()); call, we still adhere to the policy of completing parent initialization before child. and we can use a static member only, and static member of child classes are available before any child objects are created anyways. so the method is avilable and can be called.

Ravi Bhatt
  • 3,147
  • 19
  • 21
0

OK..i think, this one could be relevant that, if we are calling some member with Super, then it first try to invoke in super class and if it doesn't find same one then it'll try to invoke the same in subclass.

PS: correct me if i'm wrong

ManishS
  • 627
  • 2
  • 11
  • 21