Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can "this" be referenced/processed before the constructor has concluded?

The specific use where I thought of this problem is as follows, but it's much more generalized.

I have a custom JFrame class which also serves as an ActionListener for its components. So my constructor looks something like the following:

private JButton myButton;

public MyCustomFrame() {
    super();
    myButton.addActionListener(this);
    // ... more stuff
}

My question is, how does this actually work behind the scenes? If the constructor is what "creates" the object which is referenced by this, how can I use this before the constructor has returned? The code compiles and works perfectly fine (as far as I can tell), so the object must already "exist" in some sense, but I'm concerned that this may cause unforeseen issues. Is there any danger in passing a "partially constructed" reference to addActionListener() (or just performing any logic with it in general)? Or is there some behind-the-curtain magic happening that keeps me safe?

For example, what about things which don't have default values and must be supplied by the constructor? If I have private final String SOME_VALUE; declared, I understand that this should default to null, but the object isn't supposed to be fully formed until the constant is supplied a value within the constructor. So would the reference, despite being final, possibly have changing values?

like image 590
asteri Avatar asked Dec 16 '13 19:12

asteri


People also ask

What restriction is there on using the super reference in a constructor?

What restriction is there on using the super reference in a constructor? a. It can only be used in the parent's constructor.

Why this () must be the first statement in constructor?

because it's a rule of the language, present in the Java Language Specification: a call to another constructor in the same class (the this(...) part) or to a constructor in the super class (using super(...) ) must go in the first line.

How do you stop the initiation of an object in a constructor?

The only way to "stop" a constructor is to throw an exception.

How do you define a default constructor in Java?

What is a default constructor? A default constructor is a constructor created by the compiler if we do not define any constructor(s) for a class. Here is an example: public class Student { String firstName; String lastName; int age; public static void main(String args[]) { Student myStudent = new Student(); myStudent.


4 Answers

this does actually exist before the constructor finishes. However, allowing a reference to this to escape your object before the constructor is complete can present a danger.

What if you passed your this reference to a method that assumes your object is fully formed and ready to go? Maybe this is fine in the case of your object, but in many cases, this could be dangerous. Allowing other methods access to an object before it is ready to be used would present a serious threat to your program being able to run reliably.

like image 22
user1445967 Avatar answered Dec 03 '22 01:12

user1445967


That Java Language Specification specifies the steps of instance creation

[...]

Next, space is allocated for the new class instance. If there is insufficient space to allocate the object, evaluation of the class instance creation expression completes abruptly by throwing an OutOfMemoryError.

The new object contains new instances of all the fields declared in the specified class type and all its superclasses. As each new field instance is created, it is initialized to its default value (§4.12.5).

Next, the actual arguments to the constructor are evaluated, left-to-right. If any of the argument evaluations completes abruptly, any argument expressions to its right are not evaluated, and the class instance creation expression completes abruptly for the same reason.

Next, the selected constructor of the specified class type is invoked. This results in invoking at least one constructor for each superclass of the class type. This process can be directed by explicit constructor invocation statements (§8.8) and is described in detail in §12.5.

So by the time the constructor (which is a method) is invoked, your instance exists with default values.

For final fields, it appears those are defaulted as well if you try to access them. For example

public class Driver {

    public static void main(String[] args) {
        new Driver();
    }

    final int value;

    public Driver() {
        print(this);
        value = 3;
    }

    static void print(Driver driver) {
        System.out.println(driver.value);
    }

}

will print 0. I'll be right back with the JLS entry if I can find it.

I couldn't find anything more specific then what is above. Maybe in 4.12.4. final Variables

A final variable may only be assigned to once.

You can take to mean that default initialization puts the value to 0 or null and the assignment changes it.

like image 157
Sotirios Delimanolis Avatar answered Dec 03 '22 01:12

Sotirios Delimanolis


Once you call your constructor, your object already exists from the start, and you're just populating it with values.

The danger of this comes if the method you're passing your object to tries to use a value that you haven't declared yet in your constructor.


You also want to avoid having your constructor(and other methods for that matter) behave in such a way that the user of the constructor wouldn't expect.

If the person instantiating your object doesn't have a reason to expect the constructor to automatically bind that object to a button, than maybe you shouldn't do that.

like image 43
Sam I am says Reinstate Monica Avatar answered Dec 03 '22 01:12

Sam I am says Reinstate Monica


You are absolutely right, that is a bad thing to do as this may only be partially initialized at the time you use it.

This is why many compilers will give a warning about doing it.

like image 45
Tim B Avatar answered Dec 03 '22 01:12

Tim B