The this keyword and object references
What this points to, why two variables can share one object, and what equality really compares.
Finished reading?
Mark this session so you can track where you are.
What this points to, why two variables can share one object, and what equality really compares.
Finished reading?
Mark this session so you can track where you are.
You have been writing this.balance = balance for a couple of sessions now, copying the pattern because it works. Today we stop copying and look underneath. What is this actually pointing at? And once we know, a second, stranger question answers itself: how can two different variables turn out to be the very same object?
this refers to, and why this.field resolves the field-versus-parameter clash.null, and know exactly why calling a method on it crashes.== apart from .equals for objects, and say which one asks “same object?”.Every instance method runs on some object. When you write b.resize(99), the method resize runs, and the whole time it runs there is one particular box it is working on: the box named b. Inside the method, the word this is simply the name for that box.thismeans “the object I am running on right now”.
That single fact explains the pattern you have been copying. Look at a setter where the parameter and the field have the same name:
public class Main { public static void main(String[] args) { Box b = new Box(10); System.out.println(b.size); b.resize(99); System.out.println(b.size); }} class Box { int size; Box(int size) { this.size = size; } void resize(int size) { this.size = size; }}10 99
Inside resize there are two things both called size: the parameter that just arrived, and the field that lives in the object. A bare size means the closest one, the parameter. So this.size is how you say “not the parameter, the size belonging to this object”. The line this.size = sizereads left to right as “the field of this object gets the value of the parameter”. Run it: size goes from 10 to 99.
thisis the object the method is currently running on.this.sizealways means the field of that object, even when a parameter borrows the same name.
size already means the field, and this.size is just a longer way to write the same thing. You only need this to break a tie between a field and a parameter (or local variable) that share a name. Many people write this. all the time anyway, for clarity. Both are fine.Because this is a real, usable value, a method can also return it, which is to say it can hand back the very object it is running on. That lets you chain calls together. Watch each bump hand the counter back so the next bump can run on it.
public class Main { public static void main(String[] args) { Counter c = new Counter(); c.bump().bump().bump(); System.out.println(c.value); }} class Counter { int value; Counter bump() { this.value = this.value + 1; return this; }}3
Each bump adds one to its own value and then says return this, giving back the same counter so the next .bump() has something to run on. Three bumps, so it prints 3. Returning this is a small thing now, but it is the first time you have seen an object hand a reference to itself out into the program. Hold that thought, because references are the rest of this session.
Here is the idea that surprises almost everyone, and it is worth slowing down for. When you writeBox b = new Box(10), the variable b does not contain the box. It contains a referenceto the box. A reference is just the box's address: a note that says “the actual box is over there”. The box itself, with its size field, lives somewhere else in memory. b only knows where to find it.
Compare that to a plain int. When you write int n = 10, the variable n is the 10; it holds the value directly. An int holds a value. An object variable holds an address. That one difference is the source of everything strange that follows.
* or ->. Python is the same under the hood: a = [1, 2]; b = a makes b point at the same list, and appending through b changes what a sees too. Same idea, exactly.Now the consequence. If b holds an address, then Box a = b copies that address. Afterwards a and b hold the same address, so they point at the one same box. There is still only one box. It just has two names. Changing it through either name changes the single object both of them see. Run this and watch the object panel: one box, two arrows pointing in.
public class Main { public static void main(String[] args) { Box a = new Box(5); Box b = a; b.size = 100; System.out.println(a.size); System.out.println(b.size); }} class Box { int size; Box(int size) { this.size = size; }}100 100
We only ever touched b. We set b.size = 100 and never wrote a.size at all. Yet both lines print 100. The reason is the whole point of today: a and b are two names for one box. When two variables refer to the same object like this, we say they are aliases of each other, and the situation is called aliasing. There is no second box to copy 5 into. There is one box, and you changed it.
Assigning one object variable to another copies the reference, not the object. Both names then point at the same single object, and a change made through one is visible through the other.
Account object to a method and that method changes it, the change sticks, because the method received a copy of the reference, pointing at your one real account. Knowing this is exactly why encapsulation matters: a private field can only be changed through methods you wrote, so the object controls its own changes even when it is shared.That last point deserves a run, because it shows aliasing reaching through a method and the getters and setters you built last session. Two names, one account, a deposit made through one name and seen through the other.
public class Main { public static void main(String[] args) { Account x = new Account(100); Account y = x; y.deposit(50); System.out.println(x.getBalance()); System.out.println(y.getBalance()); }} class Account { private int balance; Account(int balance) { this.balance = balance; } void deposit(int amount) { this.balance = this.balance + amount; } int getBalance() { return this.balance; }}150 150
We deposited through y. Both x and y report 150, because there is one account and they both point at it. Notice that balance is private, so this only works through the deposit and getBalance methods. The object still guards its own field; it just happens to have two names.
A reference variable holds an address. So what is in it before you give it an object to point at? The answer is a special non-address called null. nullmeans “this variable points at no object at all”. It is the empty slot, the note with no house written on it.
You can set a reference to null on purpose, and an object field you never assigned starts asnull too. The trouble comes when you forget, and then reach through a null with the dot. There is no object on the other end, so there is nothing to reach into. Java stops the program at once. Run this and read the error it reports.
public class Main { public static void main(String[] args) { Box b = null; System.out.println(b.size); }} class Box { int size; Box(int size) { this.size = size; }}NullPointerException: tried to use an object that is null
The program halts on b.size with a NullPointerException. The dot means “reach into the object b points at”, but b points at nothing, so there is no size to read. This is one of the most common errors you will ever hit in Java. When you see it, the question to ask is always the same: which variable did I expect to hold an object, and why is it still null?
x.something, you are assuming x points at a real object. If x is null, that assumption is wrong and you get a NullPointerException. The fix is never to “ignore the error”; it is to make sure x was given an object (usually with new) before you used the dot.null with ==: if (b == null) is true exactly when b points at nothing. That comparison never crashes, because you are not using the dot, only asking what the reference holds. Reaching through the reference with a dot is the only thing that fails on null.Now that you know a variable holds a reference, the difference between == and .equals becomes clear instead of mysterious. For objects, == compares the references. It asks: are these two names pointing at the same one object? It does not look inside at the fields at all.
So two boxes that both hold 5 are still two separate boxes, at two different addresses, and== says they are not equal. Only when two variables point at the literally same object does== say true. Watch.
public class Main { public static void main(String[] args) { Box a = new Box(5); Box b = new Box(5); System.out.println(a == b); Box c = a; System.out.println(a == c); }} class Box { int size; Box(int size) { this.size = size; }}false true
a == b prints false: two separate boxes, same contents but different objects. Thenc = a makes c an alias of a, so a == c prints true: same object, same address. == is asking about identity, not about contents.
There is a second way to ask about equality: the .equals method. The promise of .equals is that it compares contents: are these two objects equal in the ways that matter, even if they are separate objects? That is the promise. But here is the honest part you need to know now: by default, before you teach a class how to compare its own contents,.equals does exactly what == does. It checks identity.
public class Main { public static void main(String[] args) { Box a = new Box(5); Box b = new Box(5); System.out.println(a.equals(b)); Box c = a; System.out.println(a.equals(c)); }} class Box { int size; Box(int size) { this.size = size; }}false true
Both lines match the == results: a.equals(b) is false (separate objects) anda.equals(c) is true (same object). So for the classes you write today, .equals and == give the same answer. That is not the final story, and it is a little surprising, so let us be precise about why.
5: false.5, by default: false.== means.null: b == null just checks the reference.b.equals(...) when b is null crashes.5”, even though they are separate objects. A class can be taught to answer that, by giving it its own version of .equals that looks at the fields. Teaching a class new behavior on top of behavior it already has is a topic of its own, and it needs ideas you have not met yet. We come back to it once you have learned inheritance and overriding. For now, the rule to carry is simple: for your classes, == and the default .equalsboth mean “same object”.Three ideas, one underlying fact. The fact is that an object variable holds a reference, an address, not the object. From there:
NullPointerException, because there is nothing on the other end to reach into..equals asks the very same thing.And tying it all back to the start: this is itself a reference, the reference to the object a method is running on. Everything today is the same single idea, looked at from a few angles.
Try each one yourself first, then open the answer.
this refer to?void resize(int size) { this.size = size; }, why is the this. necessary here, when it is optional in many other methods? Trace the broken version below, where this. was dropped, and explain the printed result.Box a = new Box(5); Box b = a; b.size = 100; System.out.println(a.size); System.out.println(b.size);NullPointerException on the line account.deposit(50);. In your own words, what does that tell you about account?Box a = new Box(5) and Box b = new Box(5). What does a == b print, and what does a.equals(b) print for this class? Why are they the same?Take these away. They continue exactly what we just did.
c = b, change c.size to 7, and print a.size, b.size, and c.size. Before you run it, write down what you expect each to be, then check. Explain in one line why all three agree.Pencil class with one field int length and a constructor. In main, declare Pencil p = null; and then try to print p.length. Run it, read the exact error, and write one sentence saying what would have to change for the line to work.Counter from the chaining example a second method Counter reset() that sets value back to 0 and returns this. Then write one chained line that bumps four times, resets, and bumps once, and predict the final value. Confirm with a print.Box a = b does not make a copy of the box. Use the words reference and address, and mention what a == b would print afterward.