Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"this" keyword: Working mechanism in Java

Tags:

java

this

After learning Java for sometime, its the first time the use of this keyword has confused me so much.

Here is how I got confused. I wrote the following code:

class BasicInheritanceTest3Base{
    private int x = 0;
    public int y;

    public void a() {
        x++;
        this.x++;
        System.out.println("BasicInheritanceTest3Base.a()");
        b();
        this.b();
        System.out.println(x);
        System.out.println(y);
    }

    public void b(){
        System.out.println("BasicInheritanceTest3Base.b()");
    }
}

public class BasicInheritanceTest3 extends BasicInheritanceTest3Base {
    private int x = 3;
    public int y = 2;

    public void b() {
        System.out.println("BasicInheritanceTest3.b()");
    }

    public static void main(String[] args){
        BasicInheritanceTest3 bit2 = new BasicInheritanceTest3();
        bit2.a();
    }
}

I got the following output:

BasicInheritanceTest3Base.a()
BasicInheritanceTest3.b()
BasicInheritanceTest3.b()
2
0

Now the first question here is: Why x and this.x point to the x of base class and not the Child class? And if this.x points to the x of the base class, why this.b() calls the b() of child class? Is the behavior of this different for fields and methods?

However, the primary concern is regarding the this keyword's mechanism. I mean you know, this points (refers) to the current object. If you think about, its not a magical behavior. There is got to be this field somewhere. For example, the .class literal for a class is invisible but is present in the emitted bytecode. Similarly, this reference should be present in the bytecode.

Alright, assuming above is true, this should be a public final (a blank final) which gets instantiated every time the object is constructed and its fields are instantiated. This implies that it is an instance variable not a static variable.

Now, if this gets instantiated to the current object's reference (which is a specific object only), how come above use of this is different for fields and methods? So all in all, what is the mechanism behind this? Does the mechanism also holds for the super keyword?

EDIT: Everyone reading the question and then the comments, I want to ask about, Where is this field declared by the compiler and what are its qualifiers. How does the resulting behavior happens behind the scenes?

like image 393
Manish Kumar Sharma Avatar asked Apr 06 '15 13:04

Manish Kumar Sharma


People also ask

How this keyword work was in Java?

The this keyword refers to the current object in a method or constructor. The most common use of the this keyword is to eliminate the confusion between class attributes and parameters with the same name (because a class attribute is shadowed by a method or constructor parameter).

What is this and this () in Java?

In Java, both this and this() are completely different from each other. this keyword is used to refer to the current object, i.e. through which the method is called. this() is used to call one constructor from the other of the same class.

Can we use this () and super () in a method?

Both “super” and “this” keywords in Java can be used in constructor chaining to call another constructor. “this()” calls the no-argument constructor of the current class, and “super()” calls the no-argument constructor of the parent class.


1 Answers

Other answers and comments have explained how fields are not polymorphic and how field access expressions are resolved based on the compile time type of the instance reference. Below, I explain how the byte code handles the this reference.

In the chapter about Receiving Arguments, the Java Virtual Machine Specification states

If n arguments are passed to an instance method, they are received, by convention, in the local variables numbered 1 through n of the frame created for the new method invocation. The arguments are received in the order they were passed. For example:

int addTwo(int i, int j) {
    return i + j;
}

compiles to:

Method int addTwo(int,int)
0   iload_1        // Push value of local variable 1 (i)
1   iload_2        // Push value of local variable 2 (j)
2   iadd           // Add; leave int result on operand stack
3   ireturn        // Return int result

By convention, an instance method is passed a reference to its instance in local variable 0. In the Java programming language the instance is accessible via the this keyword.

Class (static) methods do not have an instance, so for them this use of local variable 0 is unnecessary. A class method starts using local variables at index 0. If the addTwo method were a class method, its arguments would be passed in a similar way to the first version:

static int addTwoStatic(int i, int j) {
    return i + j;
}

compiles to:

Method int addTwoStatic(int,int)
0   iload_0
1   iload_1
2   iadd
3   ireturn

The only difference is that the method arguments appear starting in local variable 0 rather than 1.

In other words, you can either view this as not being declared anywhere or as being declared as the first parameter of every instance method. A local variable table entry is created for each instance method and populated on each invocation.

The chapter on Invoking methods states

The normal method invocation for a instance method dispatches on the run-time type of the object. (They are virtual, in C++ terms.) Such an invocation is implemented using the invokevirtual instruction, which takes as its argument an index to a run-time constant pool entry giving the internal form of the binary name of the class type of the object, the name of the method to invoke, and that method's descriptor (§4.3.3). To invoke the addTwo method, defined earlier as an instance method, we might write:

int add12and13() {
    return addTwo(12, 13);
}

This compiles to:

Method int add12and13()
0   aload_0             // Push local variable 0 (this)
1   bipush 12           // Push int constant 12
3   bipush 13           // Push int constant 13
5   invokevirtual #4    // Method Example.addtwo(II)I
8   ireturn             // Return int on top of operand stack;
                        // it is the int result of addTwo()

The invocation is set up by first pushing a reference to the current instance, this, on to the operand stack. The method invocation's arguments, int values 12 and 13, are then pushed. When the frame for the addTwo method is created, the arguments passed to the method become the initial values of the new frame's local variables. That is, the reference for this and the two arguments, pushed onto the operand stack by the invoker, will become the initial values of local variables 0, 1, and 2 of the invoked method.

like image 130
Sotirios Delimanolis Avatar answered Oct 27 '22 07:10

Sotirios Delimanolis