3.9.1 this Refers to the Current Object
You have written one class and then created several objects from it. Each object has its own copy of every instance variable (Topic 3.3). But the code for a method is written only once, inside the class. When that one piece of code says seat, how does Java know whose seat it means? And what if a method needs to talk about “the object I belong to” as a whole, not just one of its variables?
The answer starts with how you call an instance method. In Topic 3.5 you wrote calls such as hit.report(). The variable before the dot holds a reference to one particular object, and the method runs for that object. While it runs, that object is the one in charge, like the person holding the microphone at a meeting. Anyone at the meeting can say “I”, and “I” always means whoever is holding the microphone right now.
Java gives every instance method and every constructor a built-in way to say “I”. That way is the keyword this. Think of it as a variable you never declare, which stores a reference to the current object: whichever object the running method was called on, or the new object a running constructor is setting up. You also never assign to it. Java fills it in automatically at the start of each call with the reference that was used to make the call. (Here the microphone analogy stops working: at a meeting people grab the microphone whenever they like, but in Java the call itself decides who holds it, every single time.)
Key idea
Inside an instance method or a constructor, this holds a reference to the current object, the object the method was
called on or the object the constructor is building. this.seat means “the seat instance variable of the current
object.”
Here is the syntax taken apart:
| Piece | What it is |
|---|---|
this |
A keyword (always lowercase) that holds a reference to the current object |
. |
The dot operator, the same one you use in hit.report() |
seat |
An instance variable or method of the current object |
this.seat |
The current object’s seat, exactly like front.seat would be for front |
Worked example: Which seat does this.seat mean?
The Ticket class below stores one seat code. Its describe method prints this.seat. Watch which object this refers to during each call. The helper class Ticket comes first in the file, and the class with main comes after it.
class Ticket
{
private String seat;
public Ticket(String seatCode)
{
seat = seatCode;
}
public void describe()
{
System.out.println("Seat " + this.seat);
}
}
public class SeatTicket
{
public static void main(String[] args)
{
Ticket front = new Ticket("A1");
Ticket back = new Ticket("K9");
back.describe();
front.describe();
}
}Lines 20 and 21 build two objects. Picture the reference in each variable as an address:
| Variable | Holds | Object at that address |
|---|---|---|
front |
address A | Ticket with seat A1 |
back |
address B | Ticket with seat K9 |
Now follow the two calls:
| Line | Call | this holds |
this.seat |
Printed |
|---|---|---|---|---|
| 22 | back.describe() |
address B | "K9" |
Seat K9 |
| 23 | front.describe() |
address A | "A1" |
Seat A1 |
Seat K9
Seat A1On line 22 Java copies the reference in back (address B) into this, then runs line 12. this.seat follows address B and finds "K9". On line 23 the very same line 12 runs again, but now this holds address A, so it finds "A1". One method, two calls, two different current objects.
The table above claims that this holds the same reference as the variable in front of the dot. You can test that claim directly. In Topic 2.6 you learned that == on two references is true only when they refer to the very same object. The next class compares this with a parameter.
Worked example: Testing what this holds
class Ticket
{
private String seat;
public Ticket(String seatCode)
{
seat = seatCode;
}
public boolean isSameAs(Ticket other)
{
return this == other;
}
}
public class SameTicket
{
public static void main(String[] args)
{
Ticket front = new Ticket("A1");
Ticket back = new Ticket("K9");
Ticket myTicket = back;
System.out.println(front.isSameAs(front));
System.out.println(front.isSameAs(back));
System.out.println(back.isSameAs(myTicket));
}
}Line 22 makes myTicket an alias of back: both hold address B. Now trace each call to isSameAs, remembering that this comes from the variable before the dot and other comes from the argument in the parentheses.
| Line | Call | this |
other |
this == other |
|---|---|---|---|---|
| 23 | front.isSameAs(front) |
address A | address A | true |
| 24 | front.isSameAs(back) |
address A | address B | false |
| 25 | back.isSameAs(myTicket) |
address B | address B | true |
true
false
trueLine 25 is the interesting one. The argument is myTicket, a different variable from back, but it holds the same address, so inside the method this and other refer to one object and == is true. The expression this == other is self-referencing: it asks the current object to compare itself with something else.
You may have noticed that the Ticket constructor on line 7 writes plain seat, not this.seat, and it works. That is because Java quietly treats a plain instance variable name inside an instance method as this.seat. Writing this. out is optional when nothing else has the same name. You will see in 3.9.2 when it stops being optional.
Watch out
Thinking this means the newest object, or the class. Some students picture this as “the object that was created most recently”. In SeatTicket.java, back was created last (line 21), yet line 23 printed Seat A1, because the call was front.describe(). Other students picture this as the Ticket class itself, but a class has no single seat value to print; each object has its own.
this is decided fresh at every call, by the reference before the dot. To find what this refers to, look at the call that started the method, not at the order in which objects were created.
Pause and try
Question 1 · Tracing
Trace this program. Write the exact output and the value of this.steps at the end of each call to walk.
class Pedometer
{
private int steps;
public Pedometer(int start)
{
steps = start;
}
public void walk(int amount)
{
this.steps = this.steps + amount;
}
public int getSteps()
{
return this.steps;
}
}
public class StepCounter
{
public static void main(String[] args)
{
Pedometer mia = new Pedometer(100);
Pedometer leo = new Pedometer(50);
leo.walk(30);
mia.walk(5);
leo.walk(10);
System.out.println(mia.getSteps());
System.out.println(leo.getSteps());
}
}Check your reasoning
105
90| Line | Call | this refers to |
this.steps after the call |
|---|---|---|---|
| 27 | leo.walk(30) |
leo’s object |
50 + 30 = 80 |
| 28 | mia.walk(5) |
mia’s object |
100 + 5 = 105 |
| 29 | leo.walk(10) |
leo’s object |
80 + 10 = 90 |
Each call changes only the object that this refers to. mia’s object is touched once, so it ends at 105; leo’s object is touched twice, so it ends at 90. If you added all three amounts to one total, you treated this as one shared object instead of “whichever object the call was made on”.
Reread 3.9.1 this Refers to the Current Object if your trace differed.
Question 2 · Multiple choice
Using the Ticket class from SameTicket.java, a program runs back.isSameAs(front). While isSameAs runs, what does this hold?
- A reference to the object
frontrefers to - A reference to the object
backrefers to - A reference to whichever
Ticketwas created most recently - Nothing;
thisis only filled in inside constructors
Check your reasoning
B. this holds the reference used to make the call, which is the variable before the dot: back.
- A is wrong:
frontis the argument, so its reference goes into the parameterother, not intothis. - C is wrong: the order of creation never matters; only the call does.
- D is wrong:
thisexists in every instance method as well as in constructors.
Reread 3.9.1 this Refers to the Current Object if you chose differently.
3.9.2 Reaching a Hidden Instance Variable
In Topic 3.8 you learned a rule about names: when a parameter or local variable has the same name as an instance variable, that name means the parameter or local variable inside the method. Programmers like to give constructor parameters the same names as the instance variables they fill, because title is the clearest name for both. But the 3.8 rule then gets in the way. Here is what happens:
class Song
{
private String title;
private int plays;
public Song(String title, int plays)
{
title = title;
plays = plays;
}
public String getTitle()
{
return title;
}
public int getPlays()
{
return plays;
}
}
public class ShadowBug
{
public static void main(String[] args)
{
Song hit = new Song("Sunrise", 12);
System.out.println(hit.getTitle());
System.out.println(hit.getPlays());
}
}null
0
The program compiles and runs, but the song has no title and no plays. On line 8, both names title mean the parameter title, so the line copies the parameter into itself. The same thing happens to plays on line 9. The instance variables are never touched, so they keep their default values from Topic 3.4: null for a String and 0 for an int.
When a local name hides an instance variable like this, we say the instance variable is shadowed. A plain name can no longer reach it. But this can: this.title always means “the current object’s title”, because the dot tells Java to look inside an object, and parameters do not live inside objects.
Key idea
When a parameter or local variable has the same name as an instance variable, use this.name for the instance
variable and the plain name for the parameter. this.title = title; copies the parameter into the current object’s
instance variable.
Worked example: Fixing the constructor with this
The only change from ShadowBug.java is lines 8 and 9:
class Song
{
private String title;
private int plays;
public Song(String title, int plays)
{
this.title = title;
this.plays = plays;
}
public String getTitle()
{
return title;
}
public int getPlays()
{
return plays;
}
}
public class ShadowFixed
{
public static void main(String[] args)
{
Song hit = new Song("Sunrise", 12);
System.out.println(hit.getTitle());
System.out.println(hit.getPlays());
}
}Inside a constructor, the current object is the one being built. Follow line 27, new Song("Sunrise", 12):
| Step | What Java does | this.title |
this.plays |
|---|---|---|---|
| 1 | new makes a new object with default values; this refers to it |
null |
0 |
| 2 | Line 8: right side title is the parameter "Sunrise"; store it in the object |
"Sunrise" |
0 |
| 3 | Line 9: right side plays is the parameter 12; store it in the object |
"Sunrise" |
12 |
| 4 | The constructor ends; new hands the object’s reference to hit |
"Sunrise" |
12 |
Sunrise
12The getters on lines 14 and 19 write plain title and plays. Nothing shadows those names inside the getters, so they mean the instance variables, just as if they said this.title and this.plays.
Shadowing is not only a constructor problem. A method can have a parameter with an instance variable’s name too, and then a single expression can use both. That is where this shows its value most clearly.
Worked example: Using both names in one expression
class Song
{
private String title;
private int plays;
public Song(String title, int plays)
{
this.title = title;
this.plays = plays;
}
public void addPlays(int plays)
{
this.plays = this.plays + plays;
}
public void report()
{
System.out.println(title + ": " + plays);
}
}
public class SongPlays
{
public static void main(String[] args)
{
Song hit = new Song("Sunrise", 12);
hit.addPlays(5);
hit.report();
hit.addPlays(3);
hit.report();
}
}Look at line 14 inside addPlays. The parameter is named plays, so the plain name plays is the amount to add, and this.plays is the song’s running total:
| Line | Call | this.plays before |
parameter plays |
this.plays after |
|---|---|---|---|---|
| 28 | hit.addPlays(5) |
12 | 5 | 17 |
| 30 | hit.addPlays(3) |
17 | 3 | 20 |
Sunrise: 17
Sunrise: 20In report (line 19) there is no parameter, so plain plays means the instance variable. The same name means different things in different methods, and this. removes any doubt.
Watch out
Putting this on the wrong side. A student remembers that the constructor needs this but writes it on the right:
class Song
{
private String title;
private int plays;
public Song(String title, int plays)
{
title = this.title;
this.plays = plays;
}
public void addPlays(int plays)
{
this.plays = this.plays + plays;
}
public void report()
{
System.out.println(title + ": " + plays);
}
}
public class ReversedThis
{
public static void main(String[] args)
{
Song hit = new Song("Sunrise", 12);
hit.addPlays(5);
hit.report();
hit.addPlays(3);
hit.report();
}
}null: 17
null: 20Line 8 reads the instance variable (still null) and stores it in the parameter, which is thrown away when the constructor ends. Assignment copies from right to left (Topic 1.4), so the side that receives the value is the left one. The instance variable must be on the left: this.title = title;.
Pause and try
Question 3 · Debugging
This program prints null is at level 3 instead of Zara is at level 3. Find the line that causes the problem, explain why, and write the corrected line.
class Player
{
private String name;
private int level;
public Player(String name, int level)
{
name = name;
this.level = level;
}
public void show()
{
System.out.println(name + " is at level " + level);
}
}
public class PlayerBug
{
public static void main(String[] args)
{
Player hero = new Player("Zara", 3);
hero.show();
}
}Check your reasoning
Line 8 is the problem. Inside the constructor, the parameter name shadows the instance variable name, so name = name; copies the parameter into itself. The instance variable keeps its default value, null. Line 9 works because this.level names the instance variable.
Corrected line 8:
this.name = name;With that change the program prints Zara is at level 3. Java gave no error for the original, because assigning a variable to itself is legal; it just does nothing useful.
Reread 3.9.2 Reaching a Hidden Instance Variable if you missed it.
Question 4 · Multiple choice
What does this program print?
class Thermostat
{
private int target;
public Thermostat(int target)
{
this.target = target;
}
public void raise(int amount)
{
int target = this.target + amount;
System.out.println("New target: " + target);
}
public int getTarget()
{
return target;
}
}
public class ThermostatRaise
{
public static void main(String[] args)
{
Thermostat hall = new Thermostat(68);
hall.raise(4);
System.out.println(hall.getTarget());
}
}New target: 72then72New target: 72then68New target: 68then68- Nothing; a local variable cannot have the same name as an instance variable
Check your reasoning
B. Line 12 declares a new local variable named target. Its right side reads this.target (68) and adds 4, so the local variable holds 72, and line 13 prints New target: 72. But the local variable disappears when the method ends; the instance variable was never assigned, so getTarget() returns 68.
- A is wrong: it assumes line 12 changes the instance variable. Because of the type
intat the start, line 12 creates a separate local variable instead. Writingthis.target = this.target + amount;would change the object. - C is wrong: line 13 prints the plain name
target, which insideraisemeans the local variable, 72. - D is wrong: this is legal; the local name simply shadows the instance variable (Topic 3.8).
Reread 3.9.2 Reaching a Hidden Instance Variable if you chose differently.
3.9.3 Passing this as an Argument
So far this has been a way to reach the current object’s own variables. Sometimes a method needs to hand the whole current object to someone else. Think of a pet going to the vet. The pet’s visit method needs to call the vet’s examine method, and examine needs to know which pet it is examining. Inside visit, there is no variable named rex or luna; the method does not know what the caller named the object. What it does have is this.
Recall from Topic 3.6 that when an argument is a reference, the parameter receives a copy of that reference, not a copy of the object. So passing this gives the other method a reference to the current object itself. Whatever that method does through its parameter happens to the current object.
Key idea
Writing this as an argument, as in doctor.examine(this), passes a reference to the current object, so the called
method can read or change that same object.
Worked example: A pet visits the vet
class Vet
{
private String vetName;
public Vet(String vetName)
{
this.vetName = vetName;
}
public void examine(Pet patient)
{
System.out.println(vetName + " examines " + patient.getName());
patient.markHealthy();
}
}
class Pet
{
private String name;
private boolean healthy;
public Pet(String name)
{
this.name = name;
healthy = false;
}
public String getName()
{
return name;
}
public boolean isHealthy()
{
return healthy;
}
public void markHealthy()
{
healthy = true;
}
public void visit(Vet doctor)
{
doctor.examine(this);
}
}
public class VetVisit
{
public static void main(String[] args)
{
Pet rex = new Pet("Rex");
Pet luna = new Pet("Luna");
Vet ortiz = new Vet("Dr. Ortiz");
rex.visit(ortiz);
System.out.println(rex.isHealthy());
System.out.println(luna.isHealthy());
}
}Two classes work together. Vet.examine takes a Pet parameter named patient, prints its name, and marks it healthy. Pet.visit takes a Vet and calls examine, passing this. Suppose rex holds address R, luna holds address L, and ortiz holds address V.
| Step | Line | What happens | rex healthy? |
|---|---|---|---|
| 1 | 56 | rex.visit(ortiz): this = R, doctor = V |
false |
| 2 | 45 | doctor.examine(this): calls examine on V and passes R |
false |
| 3 | 12 | Inside examine, this = V and patient = R; prints the line |
false |
| 4 | 13 | patient.markHealthy(): calls markHealthy on R |
false |
| 5 | 40 | Inside markHealthy, this = R; sets R’s healthy to true |
true |
| 6 | 57 | Back in main, prints rex.isHealthy() |
true |
| 7 | 58 | Prints luna.isHealthy(); L was never touched |
true |
Dr. Ortiz examines Rex
true
falseNotice how this changes as the calls nest. In visit it is the pet R. In examine it is the vet V, because examine was called on doctor. In markHealthy it is R again, because that call was made on patient. Each method has its own this, set by its own call. The reference R traveled from this in visit into the parameter patient, which is how the vet changed Rex and only Rex.
The other method does not have to be in a different class. Here a Rectangle passes itself to another Rectangle, so one comparison method can be reused to build a second one.
Worked example: Asking the other object to compare with me
class Rectangle
{
private int width;
private int height;
public Rectangle(int width, int height)
{
this.width = width;
this.height = height;
}
public int area()
{
return width * height;
}
public boolean isLargerThan(Rectangle other)
{
return this.area() > other.area();
}
public boolean isSmallerThan(Rectangle other)
{
return other.isLargerThan(this);
}
}
public class RectangleCompare
{
public static void main(String[] args)
{
Rectangle poster = new Rectangle(4, 6);
Rectangle card = new Rectangle(3, 5);
System.out.println(card.isSmallerThan(poster));
System.out.println(poster.isSmallerThan(card));
}
}isLargerThan (line 19) compares the current rectangle’s area with another’s. isSmallerThan (line 24) does not redo that work. “I am smaller than you” means the same as “you are larger than me”, so it calls isLargerThan on the other rectangle and passes this as the argument. Trace line 34, where poster has area 4 × 6 = 24 and card has area 3 × 5 = 15:
| Step | Line | Call | this |
other |
Result |
|---|---|---|---|---|---|
| 1 | 34 | card.isSmallerThan(poster) |
card |
poster |
|
| 2 | 24 | other.isLargerThan(this) |
poster |
card |
|
| 3 | 19 | this.area() > other.area() |
poster |
card |
24 > 15: true |
Line 35 is the reverse: poster.isSmallerThan(card) becomes card.isLargerThan(poster), and 15 > 24 is false.
true
falseIn step 2 the roles swap: the object that was this in isSmallerThan becomes other in isLargerThan, and the old other becomes the new this. Line 19 also shows that this.area() calls a method on the current object; plain area() would mean the same thing.
Watch out
Passing a new object instead of this. A student does not trust this and builds a pet to send instead:
class Vet
{
private String vetName;
public Vet(String vetName)
{
this.vetName = vetName;
}
public void examine(Pet patient)
{
System.out.println(vetName + " examines " + patient.getName());
patient.markHealthy();
}
}
class Pet
{
private String name;
private boolean healthy;
public Pet(String name)
{
this.name = name;
healthy = false;
}
public String getName()
{
return name;
}
public boolean isHealthy()
{
return healthy;
}
public void markHealthy()
{
healthy = true;
}
public void visit(Vet doctor)
{
doctor.examine(new Pet(name));
}
}
public class VisitCopyBug
{
public static void main(String[] args)
{
Pet rex = new Pet("Rex");
Pet luna = new Pet("Luna");
Vet ortiz = new Vet("Dr. Ortiz");
rex.visit(ortiz);
System.out.println(rex.isHealthy());
System.out.println(luna.isHealthy());
}
}Dr. Ortiz examines Rex
false
falseLine 45 now passes new Pet(name), a brand-new object that happens to have the same name. The vet prints the right name, so the bug is easy to miss, but markHealthy changes the new object, which nobody keeps a reference to. rex’s object is still unhealthy. To let another method act on the current object, pass this, which is a reference to that object and not a copy of it.
Pause and try
Question 5 · Tracing
Write the exact output of this program.
class Friend
{
private String name;
public Friend(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void greet(Friend other)
{
System.out.println(name + " waves at " + other.getName());
other.waveBack(this);
}
public void waveBack(Friend greeter)
{
System.out.println(name + " waves back at " + greeter.getName());
}
}
public class FriendWave
{
public static void main(String[] args)
{
Friend kai = new Friend("Kai");
Friend noor = new Friend("Noor");
noor.greet(kai);
kai.greet(noor);
}
}Check your reasoning
Noor waves at Kai
Kai waves back at Noor
Kai waves at Noor
Noor waves back at Kai| Line | Call | this |
parameter | Printed |
|---|---|---|---|---|
| 33 | noor.greet(kai) |
Noor | other = Kai |
Noor waves at Kai |
| 18 | other.waveBack(this) |
Kai | greeter = Noor |
Kai waves back at Noor |
| 34 | kai.greet(noor) |
Kai | other = Noor |
Kai waves at Noor |
| 18 | other.waveBack(this) |
Noor | greeter = Kai |
Noor waves back at Kai |
Inside greet, this is the greeter, and it is passed to waveBack, where it lands in the parameter greeter. Inside waveBack, this is the friend who was greeted, so name there is that friend’s name.
Reread 3.9.3 Passing this as an Argument if your output differed.
Question 6 · Short answer
A student says, “In VetVisit.java, doctor.examine(this) sends the vet a copy of the pet, so the vet’s changes should not affect rex.” Explain why the student is wrong.
Check your reasoning
Model answer: this holds a reference to the current pet, not the pet itself. When a reference is passed as an argument, the parameter patient gets a copy of the reference, so it refers to the very same object as rex. When examine calls patient.markHealthy(), it changes that one shared object, which is why rex.isHealthy() prints true.
A complete answer says:
thisis a reference to the current object.- Passing a reference copies the reference, not the object (Topic 3.6).
- So the parameter and
rexrefer to the same object, and changes through the parameter are seen throughrex.
Reread 3.9.3 Passing this as an Argument if your answer was missing one of these.
3.9.4 Class Methods Have No this
In Topic 3.7 you wrote class methods, the methods marked static. They belong to the class as a whole and are usually called with the class name, as in Math.sqrt(16.0) or Ticket.getTicketsSold(). Ask the 3.9.1 question about such a call: which object is in charge while it runs? Look at the call. There is no object before the dot, only a class name. No object, no current object, and so nothing for this to hold.
That is why Java gives a class method no this at all. It also explains a rule you met in Topic 3.7: a class method cannot use instance variables or call instance methods directly. A plain instance variable name means this.name, and an instance method call with no dot means this.method(). Without a this, neither has an object to work on.
Key idea
A class (static) method runs without any current object, so it has no this. Using this, or a plain instance
variable or instance method name, inside a class method is a compile error.
Worked example: Asking a class method for this.seat
This version of Ticket makes getSeat static by mistake:
class Ticket
{
private String seat;
public Ticket(String seat)
{
this.seat = seat;
}
public static String getSeat()
{
return this.seat;
}
}
public class StaticThisError
{
public static void main(String[] args)
{
Ticket front = new Ticket("A1");
System.out.println(Ticket.getSeat());
}
}Java refuses to compile it:
StaticThisError.java:12: error: non-static variable this cannot be referenced from a static context
return this.seat;
^
1 errorThe message calls this a “non-static variable”, meaning it belongs to an object, and line 12 is in a “static context”, a class method. Think about line 21: Ticket.getSeat() names no ticket. If there were three tickets, Java would have no way to choose whose seat to return. (Exact error wording can vary between Java versions; the key words are “static context”.)
There are two ways to fix it, depending on what the method should do. If the method is about one ticket, remove static so it becomes an instance method, and call it on an object: front.getSeat(). If the method really belongs to the class, give it a parameter so the caller supplies the object, as the next example does.
The next example shows what a class method can do: use class variables, and work with objects that are passed to it as parameters.
Worked example: A class method that receives its objects
class Ticket
{
private static int ticketsSold = 0;
private String seat;
public Ticket(String seat)
{
this.seat = seat;
ticketsSold++;
}
public String getSeat()
{
return this.seat;
}
public static int getTicketsSold()
{
return ticketsSold;
}
public static boolean sameRow(Ticket first, Ticket second)
{
return first.seat.substring(0, 1).equals(second.seat.substring(0, 1));
}
}
public class StaticWithParameter
{
public static void main(String[] args)
{
Ticket front = new Ticket("A1");
Ticket beside = new Ticket("A2");
Ticket back = new Ticket("K9");
System.out.println(Ticket.getTicketsSold());
System.out.println(Ticket.sameRow(front, beside));
System.out.println(Ticket.sameRow(front, back));
System.out.println(back.getSeat());
}
}| Line | What happens | Printed |
|---|---|---|
| 32–34 | Three constructors run. In each, this is the new ticket; line 9 adds 1 to the class variable |
|
| 35 | getTicketsSold is a class method, but it uses only the class variable ticketsSold, which is 3 |
3 |
| 36 | sameRow(front, beside): first.seat is "A1", second.seat is "A2"; both start with "A" |
true |
| 37 | sameRow(front, back): "A" versus "K" |
false |
| 38 | getSeat is an instance method called on back, so this.seat is "K9" |
K9 |
3
true
false
K9sameRow never mentions this. It reaches each ticket’s seat through a parameter, first.seat and second.seat. (It may use the private seat directly because the parameters are Tickets and the method is inside the Ticket class, as in Topic 3.6.) The class method works with objects only when someone hands it references to them.
Here is the comparison side by side:
| Question | Instance method | Class (static) method |
|---|---|---|
| How is it usually called? | On an object: back.getSeat() |
On the class: Ticket.getTicketsSold() |
| Is there a current object? | Yes, the object before the dot | No |
Can it use this? |
Yes | No; compile error |
| How does it reach an object’s variables? | this.seat, or plain seat |
Only through a parameter: first.seat |
Watch out
Using this in main. main is a class method too: its header says public static void main. So this does not exist there, even when main is written inside the class whose objects it creates:
public class MainThis
{
private int score;
public MainThis(int score)
{
this.score = score;
}
public int getScore()
{
return this.score;
}
public static void main(String[] args)
{
MainThis game = new MainThis(40);
System.out.println(this.getScore());
}
}MainThis.java:18: error: non-static variable this cannot be referenced from a static context
System.out.println(this.getScore());
^
1 errorStudents write this because main sits inside the class, so it feels like main belongs to an object. It does not; it runs before any object exists. The fix is to call the method on the object that line 17 created: System.out.println(game.getScore());, which prints 40.
Pause and try
Question 7 · Multiple choice
Each choice is a method that could be added to this class. Which one does not compile?
public class Lamp
{
private int brightness;
public Lamp(int brightness)
{
this.brightness = brightness;
}
}public static void turnOff() { this.brightness = 0; }public static int maxBrightness() { return 100; }public void dim() { this.brightness = 0; }public static void dimLamp(Lamp lamp) { lamp.brightness = 0; }
Check your reasoning
A. turnOff is a class method, so there is no current object and no this. Java reports non-static variable this cannot be referenced from a static context.
- B compiles: a class method that uses no instance data needs no object.
- C compiles:
dimis an instance method, so it has athis, the lamp it is called on. - D compiles: the class method gets its object through the parameter
lamp, and it may use the privatebrightnessbecauselampis aLampand the method is insideLamp.
Reread 3.9.4 Class Methods Have No this if you chose differently.
Question 8 · Debugging
This program does not compile. Explain the error and write the corrected line.
public class MainCallBug
{
private int score;
public MainCallBug(int score)
{
this.score = score;
}
public void printScore()
{
System.out.println("Score: " + score);
}
public static void main(String[] args)
{
MainCallBug game = new MainCallBug(40);
printScore();
}
}MainCallBug.java:18: error: non-static method printScore() cannot be referenced from a static context
printScore();
^Check your reasoning
An instance method call with no object before the dot, such as printScore();, means this.printScore();: call it on the current object. That works inside another instance method, but line 18 is in main, a class method, which has no this. Java cannot tell which object’s score to print.
Corrected line 18:
game.printScore();Now the method runs with this referring to the object created on line 17, and the program prints Score: 40.
Reread 3.9.4 Class Methods Have No this if you missed it.
Putting it together
This bank account class uses every idea from the chapter: this. to reach shadowed instance variables in the constructor, this.balance in self-referencing expressions, this passed as an argument, and a class method that has no this. Before reading the trace, predict all five lines of output.
Worked example: Transfers between two accounts
class BankAccount
{
private static int accountsOpened = 0;
private String owner;
private int balance;
public BankAccount(String owner, int balance)
{
this.owner = owner;
this.balance = balance;
accountsOpened++;
}
public String getOwner()
{
return owner;
}
public int getBalance()
{
return balance;
}
public void transferTo(BankAccount other, int amount)
{
this.balance = this.balance - amount;
other.receive(this, amount);
}
public void receive(BankAccount sender, int amount)
{
this.balance = this.balance + amount;
System.out.println(owner + " got " + amount + " from " + sender.getOwner());
}
public static int getAccountsOpened()
{
return accountsOpened;
}
}
public class BankTransfer
{
public static void main(String[] args)
{
BankAccount ana = new BankAccount("Ana", 100);
BankAccount ben = new BankAccount("Ben", 20);
ana.transferTo(ben, 30);
ben.transferTo(ana, 5);
System.out.println(ana.getBalance());
System.out.println(ben.getBalance());
System.out.println(BankAccount.getAccountsOpened());
}
}Lines 46 and 47 run the constructor twice. In each run, this is the account being built, so lines 9 and 10 fill that account’s owner and balance from the same-named parameters, and line 11 adds 1 to the shared class variable. Now the transfers:
| Step | Line | Running | this |
Other names | Ana’s balance | Ben’s balance |
|---|---|---|---|---|---|---|
| 0 | 47 | (after both constructors) | 100 | 20 | ||
| 1 | 48 | ana.transferTo(ben, 30) |
Ana | other = Ben |
100 | 20 |
| 2 | 26 | this.balance - amount |
Ana | 70 | 20 | |
| 3 | 27 | other.receive(this, 30) |
Ana | passes Ana | 70 | 20 |
| 4 | 32 | inside receive |
Ben | sender = Ana |
70 | 50 |
| 5 | 33 | prints Ben got 30 from Ana |
Ben | sender = Ana |
70 | 50 |
| 6 | 49 | ben.transferTo(ana, 5) |
Ben | other = Ana |
70 | 50 |
| 7 | 26 | this.balance - amount |
Ben | 70 | 45 | |
| 8 | 32 | inside receive |
Ana | sender = Ben |
75 | 45 |
| 9 | 33 | prints Ana got 5 from Ben |
Ana | sender = Ben |
75 | 45 |
Then line 50 prints 75, line 51 prints 45, and line 52 calls the class method getAccountsOpened, which returns the class variable, 2.
Ben got 30 from Ana
Ana got 5 from Ben
75
45
2Three things to notice:
- In
receive, the plain nameowneron line 33 meansthis.owner, the account receiving money, whilesender.getOwner()asks the account that was passed in. Passingthison line 27 is the only wayreceivelearns who sent the money. thisflips between the two accounts as the calls nest: intransferToit is the sender, inreceiveit is the receiver.getAccountsOpenedis called with the class name and has nothis. It works because it uses only the class variable, neverownerorbalance.
Vocabulary
| Term | Definition | Example |
|---|---|---|
this |
A keyword that, inside an instance method or constructor, holds a reference to the current object | this.seat |
| current object | The object whose instance method or constructor is running: the one the method was called on, or the one being built | back in back.describe() |
| shadowed | Describes an instance variable hidden by a parameter or local variable with the same name | title in Song(String title, …) |
| self-referencing expression | An expression that uses this to refer to the current object |
this == other |
| class method | A static method; it belongs to the class, is called with the class name, and has no this |
Ticket.getTicketsSold() |
Chapter summary
- Inside an instance method or a constructor,
thisholds a reference to the current object: the object before the dot in the call, or the object a constructor is building. It is set again at every call. - A plain instance variable or instance method name inside an instance method means the same as writing
this.in front of it. - When a parameter or local variable shadows an instance variable,
this.namereaches the instance variable;this.title = title;stores a parameter in the object. Forgettingthis.compiles but leaves the instance variable unchanged. - Passing
thisas an argument hands another method a reference to the current object, so that method can read or change the same object, not a copy. - A class (static) method has no current object, so it has no
this; usingthisor an instance member directly there, including inmain, is a compile error. A class method can work with objects passed to it as parameters.
Review questions
Question 9 · Multiple choice
Which statement about the keyword this is true?
thisrefers to the object that was created most recently.- Inside a constructor,
thisrefers to the object that the constructor is building. thiscan be used inside any method, includingstaticmethods, as long as at least one object exists.- In
this.score = score;, the value of the instance variable is copied into the parameter.
Check your reasoning
B. A constructor runs as part of new, and while it runs, the new object is the current object. That is why this.title = title; in a constructor fills in the new object.
- A is wrong:
thisdepends on the call that is running, not on the order of creation. - C is wrong: a
staticmethod has no current object, sothisis a compile error there no matter how many objects exist. - D is wrong: assignment copies from right to left, so the parameter
scoreis copied into the instance variablethis.score.
Reread 3.9.1 this Refers to the Current Object, 3.9.2 Reaching a Hidden Instance Variable, and 3.9.4 Class Methods Have No this if you chose differently.
Question 10 · Tracing
The method larger returns a reference to whichever counter has the larger count. Trace the program and write the exact output.
class Counter
{
private int count;
public Counter(int count)
{
this.count = count;
}
public Counter larger(Counter other)
{
if (this.count >= other.count)
{
return this;
}
return other;
}
public void increment()
{
count++;
}
public int getCount()
{
return count;
}
}
public class LargerCounter
{
public static void main(String[] args)
{
Counter red = new Counter(7);
Counter blue = new Counter(9);
Counter winner = red.larger(blue);
winner.increment();
Counter again = red.larger(red);
again.increment();
System.out.println(red.getCount());
System.out.println(blue.getCount());
}
}Check your reasoning
8
10| Line | What happens | red count |
blue count |
|---|---|---|---|
| 34–35 | Two counters are built | 7 | 9 |
| 36 | red.larger(blue): this is red, other is blue; 7 >= 9 is false, so return other |
7 | 9 |
| 36 | winner now refers to blue’s object |
7 | 9 |
| 37 | winner.increment() changes blue’s object |
7 | 10 |
| 38 | red.larger(red): this and other are both red; 7 >= 7 is true, so return this |
7 | 10 |
| 39 | again refers to red’s object; increment changes it |
8 | 10 |
Returning this returns a reference to the current object, not a copy (Topic 3.6), so winner and again are aliases of existing counters. other.count on line 12 is allowed even though count is private, because other is a Counter and the code is inside Counter.
Reread 3.9.1 this Refers to the Current Object and 3.9.3 Passing this as an Argument if your trace differed.
Question 11 · Debugging
A student changes line 45 of VetVisit.java and gets a compile error. Explain the error and write the corrected line.
class Vet
{
private String vetName;
public Vet(String vetName)
{
this.vetName = vetName;
}
public void examine(Pet patient)
{
System.out.println(vetName + " examines " + patient.getName());
patient.markHealthy();
}
}
class Pet
{
private String name;
private boolean healthy;
public Pet(String name)
{
this.name = name;
healthy = false;
}
public String getName()
{
return name;
}
public boolean isHealthy()
{
return healthy;
}
public void markHealthy()
{
healthy = true;
}
public void visit(Vet doctor)
{
doctor.examine(doctor);
}
}
public class ExamineWrongArg
{
public static void main(String[] args)
{
Pet rex = new Pet("Rex");
Pet luna = new Pet("Luna");
Vet ortiz = new Vet("Dr. Ortiz");
rex.visit(ortiz);
System.out.println(rex.isHealthy());
System.out.println(luna.isHealthy());
}
}ExamineWrongArg.java:45: error: incompatible types: Vet cannot be converted to Pet
doctor.examine(doctor);
^Check your reasoning
examine has one parameter of type Pet, so its argument must be a Pet. The student passed doctor, which is a Vet, so the types do not match. The pet that should be examined is the current object, the one visit was called on, and inside visit the way to name it is this.
Corrected line 45:
doctor.examine(this);Inside Pet.visit, this is a Pet, so the call compiles, and the vet examines the pet that made the visit.
Reread 3.9.3 Passing this as an Argument if you missed it.
Question 12 · Multiple choice
Using the BankAccount class from Putting it together, what does this code print?
BankAccount ben = new BankAccount("Ben", 20);
ben.transferTo(ben, 10);
System.out.println(ben.getBalance());Ben got 10 from Benthen20Ben got 10 from Benthen10Ben got 10 from Benthen30- Nothing; an object cannot be passed to its own method
Check your reasoning
A. In transferTo, this and other both refer to Ben’s one account. Line 26 subtracts 10 (balance 10). Line 27 calls receive on that same account and passes this, so in receive, this and sender are also Ben. Line 32 adds 10 back (balance 20), and line 33 prints Ben got 10 from Ben.
- B is wrong: it forgets that
receiveruns on the same object and adds the money back. - C is wrong: it treats the receiving account as a separate object that started at 20, but there is only one account.
- D is wrong: nothing stops a method from receiving a reference to its own object; this one simply refers to the same object as
this.
Reread 3.9.3 Passing this as an Argument if you chose differently.
Question 13 · Tracing
Here a constructor passes this to another object. Write the exact output.
class Club
{
private int members;
private String newest;
public Club()
{
members = 0;
newest = "nobody";
}
public void join(Student s)
{
members++;
newest = s.getName();
}
public void report()
{
System.out.println(members + " members, newest: " + newest);
}
}
class Student
{
private String name;
public Student(String name, Club club)
{
this.name = name;
club.join(this);
}
public String getName()
{
return name;
}
}
public class ClubJoin
{
public static void main(String[] args)
{
Club chess = new Club();
chess.report();
Student amir = new Student("Amir", chess);
Student bea = new Student("Bea", chess);
chess.report();
}
}Check your reasoning
0 members, newest: nobody
2 members, newest: Bea| Line | What happens | members |
newest |
|---|---|---|---|
| 44 | The Club constructor sets the starting state |
0 | "nobody" |
| 45 | Prints the first report | 0 | "nobody" |
| 46 | Student constructor: this is the new student; line 30 stores "Amir"; line 31 passes this to join |
1 | "Amir" |
| 47 | Same for the second student, whose name is "Bea" |
2 | "Bea" |
| 48 | Prints the second report | 2 | "Bea" |
Inside join, s refers to the student under construction, so s.getName() returns the name line 30 just stored. Order matters: if line 31 came before line 30, join would see the name as null.
Reread 3.9.2 Reaching a Hidden Instance Variable and 3.9.3 Passing this as an Argument if your trace differed.
Question 14 · Short answer
A student adds this class method to Ticket:
public static boolean isFrontRow()
{
return this.seat.substring(0, 1).equals("A");
}Explain why it does not compile, and describe one way to change the method so that it does.
Check your reasoning
Model answer: isFrontRow is a class method, so it is called on the class, as in Ticket.isFrontRow(), with no ticket in front of the dot. A class method has no current object, so this does not exist inside it, and Java reports that this cannot be referenced from a static context. One fix is to remove static, making it an instance method that is called on a ticket, such as front.isFrontRow(); then this refers to that ticket. Another fix is to keep it static but add a parameter, isFrontRow(Ticket t), and use t.seat instead of this.seat.
A complete answer says:
- The method is static, so there is no current object and no
this. - Either make it an instance method (remove
static) or pass the ticket in as a parameter and use the parameter instead ofthis.
Reread 3.9.4 Class Methods Have No this if your answer was missing one of these.
Reflection
- I can say which object this refers to for a given method call or constructor. Reread
- I can trace self-referencing expressions such as this.steps + amount and this == other. Reread
- I can use this to store a parameter in a shadowed instance variable, and spot code that forgets to. Reread
- I can trace code that passes this as an argument and explain why the called method changes the same object. Reread
- I can explain why a class method, including main, has no this, and fix code that uses it there. Reread