7.7 The this Reference
The this keyword allows you to specify the current object, whatever it might be. This obviates the need to store a separate copy of every variable and method for each instantiation of a class. The this reference always refers to the current object. Let's say it another way. When writing this.name, this refers to the current object's copy of name.
When you invoke a new object method from the same object, there is no need to prefix a this reference. The this reference is included implicitly by Java:
public class Test { void someMethod() { anotherMethod(); } void anotherMethod() { // method definition here... } }
In the above code, anotherMethod is invoked without an object reference. This code would compile because when the compiler encounters an unreferenced method, it first looks in the local class for one that matches it. If it finds a matching method, the keyword this is prefixed to the method call implicitly.
So the above code is actually the equivalent to writing the following:
public class Test { void someMethod() { this.anotherMethod(); } void anotherMethod() { // method definition here... } }
Again, you don't need to (and shouldn't) write this out in this manner; Java does it for you in such cases.
NOTE
You can only use the object reference this inside a non-static method. Try to think of why that might be. To figure it out, we'll have to remember what a static method is. A static method is a class-level method; that is, it is associated with the type and not a specific object: It doesn't make any sense to refer to this when there is no object.
7.7.1 this, the Implicit Parameter
While it sounds straightforward enough, this can be tough to get your mind around, and there is another common occurrence of the this keyword, which we'll examine now. As you know, one job of methods is to set values in objects. In order to set those values, the object must have a variable defined to hold that data; those variables, as you'll recall, are referred to as data fields or fields. You often will use this in conjunction with method parameters to set field values.
Let's say we have a class with a field named hireDate. We write a method that accepts a parameter called hireDate. hireDate will then refer to the parameter, and not to the object instance's data field of the same name. this refers to the implicit parameterthat of the object being constructed. Using this effectually hides the parameter, and it allows us to refer to the data field. It is therefore very common to see the implicit parameter used in the following fashion.
You may remember our Employee code from Chapter 5; the setter methods look like so:
protected void setHireDate(Date hd) { hireDate = hd; } protected void setSalary(double s) { salary = s; }
We can use this, instead, to accomplish the same task with less confusion:
public void setHireDate(Date hireDate) { this.hireDate = hireDate; } public void setSalary(double salary) { this.salary = salary; }
7.7.2 Calling Other Constructors with this
There is another use for this. Constructors create objects, and an object can have a different constructor based on what the programmer wishes to do with the object at that time. For example, as we know, there are two constructors for the java.lang.boolean wrapper class: Boolean(boolean value) and Boolean(String s). You can quickly see how it is often the case that writing different constructors could involve writing code that is common to the different constructors more than once. This can be tedious to write and difficult to debug and maintain. this offers a solution by allowing one constructor to call another.
If a constructor's first statement is prefixed with this(...), the constructor will call another constructor within the same class.
NOTE
Having one constructor call another in this way is called "explicit constructor invocation."
Here's how it works. Let's say we're going to add a clearance level to our Employee object that indicates something about the employee's security clearance. We can create a separate constructor that just sets the security level. The following two listings, MakeEmployee1.java and Employee.java, show this:
7.7.3 MakeEmployee1.java
package chp7; import java.util.Date; public class MakeEmployee1 { public static void main(String[] args){ // create a new Employee object Employee1 myEmp = new Employee1(444, "Medium"); System.out.println("Employee created with ID: " + myEmp.getEmployeeID()); // set its salary myEmp.setSalary(75000.00); // get its salary System.out.println("Salary: " + myEmp.getSalary()); // test if our this() constructor call worked. // we know it works because we have not set this // variable // anywhere but in the this("Low") constructor call. // We know both constructors are called because // we get our employee ID set too. System.out.println("Clearance Level: " + myEmp.getClearanceLevel()); } }
Notice that we have modified how the Employee object is constructed here, so looking back on the Chapter 5 example will likely only be confusing. Instead of randomly generating an EmployeeID, which is obviously not a great idea, we are supplying it explicitly. We now have two constructors for the Employee object that you can call. The first option is to supply only an EmployeeID. That constructor will then call the second constructor, passing it a value for the ClearanceLevel parameter. The idea is that when the second constructor is called by this(...), then it will automatically supply that value. This is admittedly a mildly contrived example, but it demonstrates how this works in this context:
7.7.4 Employee1.java
package chp7; import java.util.Date; public class Employee1 { private int employeeID; private Date hireDate; private double salary; // added for this constructor calling demo private String clearance; // two constructors. one in which only the EmpID is set, // and another in which the EmpID and the // SecurityLevel are set // default constructor creates only EmpID public Employee1(int employeeID) { // call the other constructor // must be first statement in constructor this(employeeID, "Low"); } public Employee1(int employeeID, String clearance) { this.employeeID = employeeID; this.clearance = clearance; } public String getClearanceLevel() { return clearance; } public int getEmployeeID() { return employeeID; } public Date getHireDate() { return hireDate; } public double getSalary() { return salary; } public void setHireDate(Date hireDate) { this.hireDate = hireDate; } public void setSalary(double salary) { this.salary = salary; } }
Run this example two different ways, and you'll get two different results. Calling this constructor
// create a new Employee object Employee1 myEmp = new Employee1(444, "Medium");
provides both the EmployeeID and the ClearanceLevel. So only that one constructor is called, and the output is this:
Employee created with ID: 444 Salary: 75000.00 Clearance Level: Medium
The ClearanceLevel specified is used directly in the constructor that expects to be passed both parameters.
If we ran this program exchanging that line in the MakeEmployee1 file with this line...
// create a new Employee object Employee1 myEmp = new Employee1(555);
...then the output is
Employee created with ID: 555 Salary: 75000.00 Clearance Level: Low
That's because we call the first constructor, which calls the second constructor, passing the string ClearanceLevel on in.
Any expressions used as arguments for this kind of explicit constructor invocation must not refer to fields or methods in the current object because there isn't really a current object yetit hasn't been constructed.
This is a useful way of handling code common to multiple constructors.