Constructor Invocation Mechanism

constructor invocation mechanism

Case 1)

m is interpreted as a function return my and taking no arguments.
To see the expected output remove () i.e use my m;

Case 2)

This is something better known as the "Most vexing parse".

n is interpreted as a function returning my that takes an argument of type pointer to function returning my taking no arguments.

To see the expected output in this case try my n((my())); [Instead of treating as an argument specification as in the former case the compiler would now interpret it as an expression because of the extra ()]

My interpretation:

my n((my())) is equivalent to my n = my(). Now the rvalue expression my() creates a temporary[i.e a call to the default constructor] and n is copy initialized to that temporary object[no call to the copy-ctor because of some compiler optimization]

P.S: I am not 100% sure about the last part of my answer. Correct me if I am wrong.

How does constructor return if it doesn't have any return type?

Constructors don't return anything. A constructor simply initializes an instance.

A new instance creation expression

new SomeExample();

produces a reference to a new instance of the specified class

A new class instance is explicitly created when evaluation of a class
instance creation expression (§15.9) causes a class to be
instantiated.

and invokes the corresponding constructor to initialize the created instance

Just before a reference to the newly created object is returned as the
result, the indicated constructor is processed to initialize the new
object using the following procedure:

  1. Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.

  2. If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then
    evaluate the arguments and process that constructor invocation
    recursively using these same five steps. If that constructor
    invocation completes abruptly, then this procedure completes abruptly
    for the same reason; otherwise, continue with step 5.

  3. This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If
    this constructor is for a class other than Object, then this
    constructor will begin with an explicit or implicit invocation of a
    superclass constructor (using super). Evaluate the arguments and
    process that superclass constructor invocation recursively using these
    same five steps. If that constructor invocation completes abruptly,
    then this procedure completes abruptly for the same reason. Otherwise,
    continue with step 4.

  4. Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable
    initializers to the corresponding instance variables, in the
    left-to-right order in which they appear textually in the source code
    for the class. If execution of any of these initializers results in an
    exception, then no further initializers are processed and this
    procedure completes abruptly with that same exception. Otherwise,
    continue with step 5.

  5. Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the
    same reason. Otherwise, this procedure completes normally.

Why is constructor of super class invoked when we declare the object of sub class?

Because it will ensure that when a constructor is invoked, it can rely on all the fields in its superclass being initialised.

see 3.4.4 in here

Invoking virtual method in constructor: difference between Java and C++

Both approaches clearly have disadvatages:

  • In Java, the call goes to a method which cannot use this properly because its members haven’t been initialised yet.
  • In C++, an unintuitive method (i.e. not the one in the derived class) is called if you don’t know how C++ constructs classes.

Why each language does what it does is an open question but both probably claim to be the “safer” option: C++’s way prevents the use of uninitialsed members; Java’s approach allows polymorphic semantics (to some extent) inside a class’ constructor (which is a perfectly valid use-case).

Can I call methods in constructor in Java?

Better design would be

public static YourObject getMyObject(File configFile){
//process and create an object configure it and return it
}
  • Factory design pattern

What properties are guaranteed by constructors in Java?

All of these facts violate my intuition of what I thought a constructor is.

They shouldn't. A constructor does exactly what you think it does.

1: uninitialized objects can be leaked by sharing this

3: uninitialized objects can be leaked to another thread before they're fully constructed

The problem with the leaking of this, starting threads in the constructor, and storing a newly constructed object where multiple threads access it without synchronization are all problems around the reordering of the initialization of non-final (and non-volatile) fields. But the initialization code is still done by the constructor. The thread that constructed the object sees the object fully. This is about when those changes are visible in other threads which is not guaranteed by the language definition.

You might be tempted to say that you know that constructor of X finished and you have a valid object, but this is untrue if you pass X to another thread - the other thread may see the uninitialized version (i.e what you just said is no different than the guarantees of calling a factory).

This is correct. It is also correct that if you have an unsynchronized object and you mutate it in one thread, other threads may or may not see the mutation. That's the nature of threaded programming. Even constructors are not safe from the need to synchronize objects properly.

2: uninitialized objects can be leaked by a subclass accessing it from the finalizer

This document is talking about finalizers and improperly being able to access an object after it has been garbage collected. By hacking subclasses and finalizers you can generate an object that is not properly constructed but it is a major hack to do so. For me this does not somehow challenge what a constructor does. Instead it demonstrates the complexity of the modern, mature, JVM. The document also shows how you can write your code to work around this hack.

What properties are guaranteed by constructors in Java?

According to the definition, a constructor:

  1. Allocates space for the object.
  2. Sets all the instance variables in the object to their default values. This includes the instance variables in the object's superclasses.
  3. Assigns the parameter variables for the object.
  4. Processes any explicit or implicit constructor invocation (a call to this() or super() in the constructor).
  5. Initializes variables in the class.
  6. Executes the rest of the constructor.

In terms of your 3 issues, #1 and #3 are, again, about when the initialization of non-final and non-volatile fields are seen by threads other than the one that constructed the object. This visibility without synchronization is not guaranteed.

The #2 issue shows a mechanism where if an exception is thrown while executing the constructor, you can override the finalize method to obtain and improperly constructed object. Constructor points 1-5 have occurred. With the hack you can bypass a portion of 6. I guess it is in the eye of the beholder if this challenges the identity of the constructor.

Java explicit constructor invocation & Instance Initializer

EDIT: It turns out the JLS is accurate after all, although it's hard to read. It's all detailed in section 12.5:

Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:

  1. Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.

  2. If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.

  3. This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.

  4. Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.

  5. Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.

Note the highlighted part - the chained constructor is executed, and then we skip step 4, which would execute the instance initializer.

Reality is that the instance and field initializers are only executed once, as you can tell from your output.

Informally, I believe it's accurate to describe the procedure as:

  • Keep chaining constructors within the same class (this(...)) until you reach a constructor body which doesn't start with this.
  • Execute the appropriate super-constructor
  • Execute the instance variable initializers and the instance initializers
  • Execute the body of the "innermost" constructor
  • Keep popping the stack of constructor bodies until you end up with the "entry" constructor

Is that MEANS use this() within subclass's constructor will implicit remove the default call to the no-argument constructor of the superclass?

Yes. Somewhere in your chain of constructors within the class, you're guaranteed to end up with a constructor which calls super either implicitly or explicitly. That is the only superclass constructor which is called.

EDIT: Note that the tutorial you quoted is demonstrably incorrect.

Sample class:

public class Test {
    {
        System.out.println("Foo");
    }
    
    public Test() {
    }
    
    public Test(int i)  {
        this();
    }
}

Output from javap -c:

public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>": ()V
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String Foo
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: return

public Test(int);
Code:
0: aload_0
1: invokespecial #5 // Method "<init>":()V
4: return
}

As you can see, the constructor for Test(int) does not have the code for the instance constructor compiled into it.

Basically, only constructors which directly call superclass constructors have instance initializer code copied into them. All other constructors will end up causing the instance initializer code to be executed via a constructor which calls the superclass constructor, of course.

How can I call the constructor of the grand parent class?

use super() from C and from B to access A's constructor

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

class B extends A {
public B() {
super();
System.out.println("B");
}
}

class C extends B {
public C() {
super();
System.out.println("C");
}
}

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

Output :

A

B

C

Note:

If all are default constructor then no need to write super(); it will implicitly call it.

If there is parametrized constructor then super(parameter.. ) is needed



Related Topics



Leave a reply



Submit