30

Consider:

class TestParent{
  public int i = 100;
  public void printName(){
    System.err.println(this); //{TestChild@428} according to the Debugger.
    System.err.println(this.i); //this.i is 100.
  }
}

class TestChild extends TestParent{
  public int i = 200;
}

public class ThisTest {
  public static void main(String[] args) {
    new TestChild().printName();
  }
}

I know that similar questions have been asked, but I couldn't get a firm understanding of the 'this' variable in Java.

Let me try to explain how I understand the result of the above image.

  1. Since it's a new TestChild() object that's calling the printName() method, the this variable in line 6 is set to a TestChild object - {TestChild@428} according to the Debugger.

  2. However, since Java doesn't have a virtual field - I'm not completely sure what this means, but I conceptually understand it as being the opposite of Java methods, which support Polymorphism - this.i is set to 100 of TestParent at compile time.

  3. So no matter what this is, this.i in a TestParent method will always be the i variable in the TestParent class.

I'm not sure that my understanding is correct so please correct me if I'm wrong.

And also, my main question is,

How is the this variable set to the current object that's calling the method? How is it actually implemented?

GhostCat
  • 137,827
  • 25
  • 176
  • 248
Kevin Park
  • 318
  • 5
  • 8
  • When you do, try to add a `toString()` method (http://www.javapractices.com/topic/TopicAction.do?Id=55) to `TestParent` and see what `System.out.println(this); yields.` – c0der Sep 16 '16 at 12:39
  • IMO `this` represents current class so if you are binding anything to `this`, means you are binding to current `class` and for that you need `variable/instance/member/method...` of current class. – bananas Sep 16 '16 at 12:42
  • 4
    Kevin Park, your understanding in items 1. through 3. is correct. @AsteriskNinja, `this` referes to an object, not a class (in a number of situation the distinction is unimportant, in others it is crucial). – Ole V.V. Sep 16 '16 at 12:45
  • argh... I ment was object of current class, my bad :) – bananas Sep 16 '16 at 12:48
  • @OleV.V. Thanks! Really helped me a lot. – Kevin Park Sep 16 '16 at 13:13

5 Answers5

43

In essence, there is no difference between

this.foo()

and

anyObject.foo()

as both are "implemented" the same way. Keep in mind that "in the end" "object orientation is only an abstraction, and in "reality" what happens is something like:

foo(callingObject)

In other words: whenever you use some object reference to call a method ... in the end there isn't a call on some object. Because deep down in assembler and machine code, something like "a call on something" doesn't exist.

What really happens is a call to a function; and the first (implicit/invisible on the source code level) parameter is that object.

BTW: you can actually write that down in Java like:

class Bar {
   void foo(Bar this) { ... }

and later use

new Bar().foo();

And for this.fieldA, in the end: you have a reference to some location in memory; and a table that tells you on which "offset" you will find fieldA.

Edit - just for the record. If you are interested in more details about foo(Bar this) - you can turn to this question; giving the details in the Java spec behind it!

Community
  • 1
  • 1
GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 9
    Exactly. Some languages make this mechanism more explicit (say Python, where you *have* to specify the `self` argument in methods) – Bakuriu Sep 16 '16 at 14:07
  • 9
    WHAT! I didn't know you could write a `this` parameter like that (though I did know that it was used internally with inner classes). That example still isn't legal though since it's missing a return type (and a method body), though. – Pokechu22 Sep 16 '16 at 14:30
  • 2
    @Pokechu22 Thanks for making me aware; fixed my example. And yes, very few people know that you can write down methods like that. – GhostCat Sep 16 '16 at 14:34
  • 2
    Neat, I learned something new today. Is this syntax actually useful for anything at the Java level? In other words, could you do anything with a `this` parameter that you couldn't do without it? – Kevin Workman Sep 16 '16 at 16:08
  • 2
    @KevinWorkman You just saw one useful thing about this construct: it is a good nerd party gag; as you can really surprise a lot of people with that. But, seriously, see another answer of mine http://stackoverflow.com/questions/39439308/java-lambda-expression-parameters/39439458#39439458 that shows how this subtle detail actually helps with certain lambda expressions. If you like what you learn there, consider upvoting that one answer, as that would help me hitting the 200-cap today. And I love hitting the daily cap ;-) – GhostCat Sep 16 '16 at 16:20
  • In both answers you make references to assembler etc, how it's impossible to call something "on" an object in assembler, but that's hardly relevant. If anything, the entire Java method may never be transformed to machine code -- it may simply be interpreted (typically in its bytecode form) by the runtime environment. And needless to say, running something on the CPU and running something inside an interpreter that runs on the CPU are very different things. In other words, it feels like a needless detail that's bound to be inaccurate because it's a simplification of the truth. – Theodoros Chatzigiannakis Sep 16 '16 at 18:15
  • @TheodorosChatzigiannakis Sure. I guess my main point there is to remind people that "object orientation" is an **abstraction**. And this abstraction is implemented that way. – GhostCat Sep 16 '16 at 18:35
  • @Bakuriu Also in Perl, where the object on which the method is invoked is implicitly passed in as the first argument of the argument list down at the byte-code level. – tchrist Sep 17 '16 at 13:28
  • @KevinWorkman you need the explicit `this` also if you need to add type annotations, like [this](https://github.com/hacklabmikkeli/membertrack/blob/master/membertrack-webapp/src/main/java/fi/ilmoeuro/membertrack/ui/MtPage.java#L67). – Ilmo Euro Sep 19 '16 at 12:36
18

What's happening here is that there are two completely different fields both called i; to use their full names, one is TestParent::i and one is TestChild::i.

Because the method printName is defined in TestParent, when it refers to i, it can only see TestParent::i, which is set to 100.

Whereas when you set i to 200 in TestChild, both fields called i are visible, but because they have the same name, TestChild::i hides TestParent::i, and you end up setting TestChild::i and leaving TestParent::i untouched.

Tom Anderson
  • 46,189
  • 17
  • 92
  • 133
  • 5
    This is the real answer to the question. I don't know why the other answers are talking about method arguments. – Nayuki Sep 16 '16 at 14:17
  • @Nayuki Because this answer would be incomplete without noteing that the this keyword is just a normal argument – Taemyr Sep 17 '16 at 11:00
1

Well when a new object is created that object has an address in memory so you can think of it as if the object had a private member this that is set to the address when the object is created. You can also think of it like this: obj.method(param) is just syntactic sugar for method(obj, param); and this is actually a parameter of method.

mroman
  • 1,354
  • 9
  • 14
0

To directly address what you see in the output: The call to print 'this.i' is passing as argument to 'print()' the value of the field 'i' in the current scope, which is the scope of the parent class. By contrast, the call to print 'this' is getting translated under the hood to a call to print 'this.getClass().getName()' [roughly speaking], and the 'getClass()' call gets the actual class object, which is for the child class.

PMar
  • 1
0

Adding some more info on top of @Tom Anderson answer, which explains hiding concept nicely.

I have added one more constructor in Child ( TestChild) which prints values of i in both parent and child.

If you want get value of i from child (TestChild), override the method in TestChild.

class TestParent{
  public int i = 100;
  public void printName(){
    System.err.println("TestParent:printName()");
    System.err.println(this); //{TestChild@SOME_NUM} according to the Debugger.
    System.err.println(this.i); //this.i is 100.
  }
}

class TestChild extends TestParent{
  public int i = 200;
  public TestChild(){
    System.out.println("TestChild.i and TestParent.i:"+this.i+":"+super.i);
  }
  public void printName(){
      //super.printName();
      System.err.println("TestChild:printName()");
      System.err.println(this); //{TestChild@SOME_NUM} according to the Debugger.
      System.err.println(this.i); //this.i is 200.
  }
}

public class ThisTest {
  public static void main(String[] args) {
    TestParent parent = new TestChild();
    parent.printName();
  }
}

Case 1: If I comment super.printName() call from child, Child version of TestChild.printName() prints the value of i in TestChild

Output:

TestChild.i and TestParent.i:200:100
TestChild:printName()
TestChild@43cda81e
200

Case 2: TestChild.printName() calls super.printName() as the first line in printName() method. In this case, i value from both parent and child are displayed in respective methods.

Output:

TestChild.i and TestParent.i:200:100
TestParent:printName()
TestChild@43cda81e
100
TestChild:printName()
TestChild@43cda81e
200
Ravindra babu
  • 37,698
  • 11
  • 250
  • 211