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?
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).
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.
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.
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 theaddTwo
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 theaddTwo
method is created, the arguments passed to the method become the initial values of the new frame's local variables. That is, the reference forthis
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With