简体   繁体   中英

Using 'this' when initializing an instance variable in java?

I've seen this used when declaring an instance variable in IB's API, but this seems like a bad idea. Is assignment guaranteed to occur after this has been fully constructed? Does it matter?

I thought that instance variables were initialized prior to the constructor since they can be used by the constructor. Is there some exception if this is used?

If it somehow works, it doesn't seem like a good idea - is second available to FirstClass 's constructor? If so, is SecondClass constructed prior to FirstClass ? Which means that num ends up being 3 and i is 10? Or will there be a runtime error? Is there even any guarantee either way?

public class FirstClass {

    SecondClass second = new SecondClass(this);
    public int i = 3;

    FirstClass(){
        i = second.DoSomething();
    }
}

public class SecondClass{

    private int num = 10;

    SecondClass(FirstClass first){
        num = first.i;
    }
    public int DoSomething(){
        return num;
    }
    ...
}

I'd like to think that IB have a pretty solid team of developers and know what they're doing. What do you think:

  1. Can this be used initializing instance variables?
  2. Should it be done?

EDIT

The answer is yes there is a guaranteed result (for now - but read on...), but no it shouldn't be done since its easy to inadvertently make changes to the code that could change this 'guaranteed' result.

I now know that when constructing a new object (such as FirstClass ) the JVM:

  1. Allocates memory for all the instance variables of this class and all its superclasses.
  2. Initializes all the variables with default values
  3. "Properly" initializes all the variables in textual order starting from its highest superclasses (ie Object) and finishing with this class - so second is initialized (ie constructed) prior to i being initialized to 3.
  4. If any of these "proper" initializations call another method or another constructor, this is done before the first constructor is invoked - so second is constructed and returned prior to the FirstClass constructor being run.
  5. If another constructor is invoked by instance initialization (such as in item 4), it goes through the same process from item 1 (memory allocation, initialization etc)

The reason doing this is bad is that the 'guaranteed' result can change in two ways:

  1. If the source code order is changed (eg if i is initialized before second is constructed) the result will change. It would be easy not to notice this.
  2. If someone subclasses our class (or classes), they may not be aware of the delicate balance and could change something that affects the result when overwriting.

So it looks like IB's API suffers from this delicacy, which I will have to now keep in mind.

Thanks to Emil H's answer and his direction to this article which ultimately led me to my understanding above: http://www.artima.com/designtechniques/initializationP.html - this is highly recommended reading.

Also thanks to Alexander Drobyshevsky who made a very similar point in his answer.

Sure, sometimes you have to use this, for example when a variable given through the Constructor is called as same as a instance variable:

int count;
public Test(int count){  
    this.count = count;
}

Yes, this (being internally a "pointer" to the object in construction) can be used in a constructor.

A stupid example could be a self refering class

class MySelf {
    Myself _me;
    public MySelf() { _me = this; }
}

And there are more realistic examples. Imagine that you want a class to represent a mathematical graph, that you would initialize with a single element connected to itself.

Interesting question. What happens in the case you describe is that when you call new FirstClass() the class loader looks up that class and loads it if its not loaded already. Then it seems to create a instance of FirstClass where all the fields have their default values, eg second is null and i is 0.

Then it the classloader loads SecondClass . An instance of SecondClass is created and initialized which sets the num field to 10. Then the constructor get's called passing in the FirstClass instance (the fields have the values that have been initialized up to that point). So the value of num in SecondClass will be set to 0 in the constructor.

After the SecondClass constructor is finished the object get's assigned to the second field in the FirstClass (which is still being initialized). After that i gets initialized to 3. Now the constructor of FirstClass get's called and i is assigned the return value of DoSomething() which in this case will be 0.

For further reference you could look at: http://www.artima.com/designtechniques/initializationP.html It gives a description of object initalization.

TL;DR So to answere your questions:

  1. Yes, this can be used when initializing instance variables.
  2. It should be avoided, since if someone moves the fields around (for instance sorting them) could change the behaviour of your class, which would not be expected.

this has two possible variations in Java; first, is when you wish to refer to a global variable (from an outer scope). eg:

class First {

    private int x;

    public void setX(int x) {
    this.x = x; // outer x is equal to the parameter
    }

}

another use, is to refer to the current object's constructor. eg:

public Color {

    private int red, green, blue;

    public void Color(int r, int g, int b) {
    red = red;
    green = g;
    blue = b;
    }

    public void setColor(int r, g, b) {
    this(r,g,b);
    } 

}

By calling this(1,2,3) with 3 parameters to tell the compiler you wish to refer to the constructor that takes 3 parameters.

Hope that helps!

At first, its a bad way to do so. You have to avoid these constructions: using not completely initialized variables. Some time it might be helpful to use "Template Method" design pattern.

Exactly for your example, in this case, result value of i = 0 after FirstClass constructor have executed; And it depends on order of assignment of i in FirstClass. If you change assignment order by next way:

public int i = 3;
SecondClass second = new SecondClass(this); // just order changed

You get answer i = 3 after FirstClass constructor have executed.

this - is only reference to current class instance. Its already exist at a time your class instance variable are beginning initializing. At first all variables have their default values (object is null, int is 0, etc.). At second are initialized all simple assignments like "int i = 3", after that executing super constructor of your current class and finally executing constructor of current class. Simple variables are initialized by order in the class up to down, BUT you should not rely on it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM