3

I was learning about using this() to call an overloaded constructor and came across this restriction:

You can not use any instance variable of the constructor's class in a call to this()

For example:

class Test{
    int x;

    public Test() {
        this(x); //Does not compile
    }

    public Test(int y) {}

    void method1() {
        method2(x); //OK
    }

    void method2(int y) {}
}

I know that no need to pass an instance field to a constructor since it's visible by default. However, why is the same restriction not applied to instance methods?

4
  • 1
    In short: the constructor constructs the class. So it seems unsafe to assume that something within this class already exists. In this case x. Commented Nov 16, 2019 at 20:57
  • A more interesting test is of course calling method2 from the constructor rather than from a method. Commented Nov 16, 2019 at 21:01
  • Related: stackoverflow.com/a/12308755/2550406 And: stackoverflow.com/questions/3685506/… Commented Nov 16, 2019 at 21:03
  • stackoverflow.com/a/14806340/2550406 states that instance variable initialization happens at the start of the constructor. That invalidates my first comment - if the variables are already initialized then. Commented Nov 16, 2019 at 21:08

3 Answers 3

5

There is one other requirement in Java: constructor calls (using this() must be performed first within any constructor. The constructors will initialize the object.

After that the instance fields are initialized after these initial calls. So as the field values are now well defined, you can use them for anything including calling other methods.

However, before the initial constructor calls, the fields are in an undefined state and cannot be used as argument for other constructor calls.


For these kind of things you need to look in the JLS:

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

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

So the instance variables are only initialized after the constructor calls. This makes sense, because it would be strange to first assign it the default value (zero or null) and then assign it another value from within a constructor.

Sign up to request clarification or add additional context in comments.

Comments

2

The constructor constructs the instance. So we shouldn't expect the instance variable x to be initialized at the time the constructor starts.
On the other hand, an instance method can already access instance variables. There is no reason to forbid passing them as a parameter into another instance method.

However, when we start to think about it a bit further, that restriction on the constructor doesn't make that much sense anymore. We are able to access instance variables there as well, so why shouldn't we be able to pass it a parameter to another constructor?

So a better question to ask is: Why can't we pass an instance variable to call an overload of our constructor from within our constructor?
And this question has been beautifully answered. It could even be considered a duplicate (but since that pretext is neccessary to understand why, I wrote an answer instead of simply flagging).

The instance fields that are declared and initialized in the class body outside of the constructor, like your int x; are assigned after the call to the overloaded constructor.

You can compare that to the other restriction we have about calling constructor overloads: We can only do so in the first line of our constructor. Right at the start. And then, the variables are not yet initialized. But they are initialized right before the first non-constructor-call instruction.


Tangential information to why this other restriction is a thing can be found here and there:

Because the JLS says so. Could the JLS be changed in a compatible manner to allow it? Yup.

 

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.

Comments

0

You should be educating the class order initialization f.e: https://www.baeldung.com/java-initialization

The int x field is not initialized when the class constructor is called. You can set (initialize) `int x 'in your constructor, but not call it.

I don't know why you have to follow this way, but you can use the static field: class Test{ static int x;

    public Test() {
        this(x); //Does not compile
    }

    public Test(int y) {}

    void method1() {
        method2(x); //OK
    }

    void method2(int y) {}
}

You can also initialize a static field in the line where it is called,

static int x =4/2;

or in static block:

static int x;
static {
    x = 4/2;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.