this Keyword
Inside an instance method or constructor, this is a reference to the current object: the object whose method is running or which is being constructed. It is how an object refers to itself. this is used to tell fields apart from parameters with the same name, to call another constructor, to pass the current object to other code, and to return the object for method chaining.
What this Refers To
public class Lamp {
private boolean on;
public void toggle() {
this.on = !this.on;
}
}
Lamp kitchen = new Lamp();
Lamp hallway = new Lamp();
kitchen.toggle(); // inside toggle, this == kitchen
hallway.toggle(); // inside toggle, this == hallway
There is only one copy of toggle's code, but every call runs on a specific object. The JVM passes that object as a hidden argument, and this is the name for it. When the method reads this.on, it reads the field of whichever lamp the method was called on.
Writing this. before a field is usually optional. on = !on; means exactly the same thing, because the compiler resolves an unqualified field name to the current object's field. this becomes required in the situations below.
Distinguishing Fields from Parameters
When a parameter or local variable has the same name as a field, the local name shadows the field: the simple name refers to the parameter. this.name refers to the field.
public class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name; // field = parameter
this.salary = salary;
}
public void setSalary(double salary) {
if (salary < 0) throw new IllegalArgumentException("salary cannot be negative");
this.salary = salary;
}
}
Using the same names for parameters and fields is the standard Java convention, because the parameter name then documents exactly which field it sets. this. resolves the ambiguity.
The silent bug without this
public Employee(String name, double salary) {
name = name; // assigns the parameter to itself
salary = salary; // same
}
This compiles. Both lines assign each parameter to itself, and the fields remain at their defaults (null and 0.0). The object appears to be created successfully but contains no data. IDEs warn about self-assignment, and declaring fields final turns it into a compile error, because the compiler then notices the final field is never assigned. Both are reasons to make fields final wherever possible.
Calling Another Constructor: this(...)
this(...) with arguments calls another constructor of the same class. It lets several constructors share one implementation:
public class Connection {
private final String host;
private final int port;
private final int timeoutSeconds;
public Connection(String host, int port, int timeoutSeconds) {
this.host = host;
this.port = port;
this.timeoutSeconds = timeoutSeconds;
}
public Connection(String host, int port) {
this(host, port, 30); // default timeout
}
public Connection(String host) {
this(host, 443); // default port
}
}
new Connection("api.example.com") runs the one-argument constructor, which calls the two-argument one, which calls the three-argument one. Only the last actually assigns fields.
Note the two different uses of the same keyword:
| Syntax | Meaning |
|---|---|
this.field | The current object's field |
this.method() | Call a method on the current object |
this(...) | Call another constructor of this class (only inside a constructor) |
Constructor chaining is covered in detail in the Constructor Overload lesson.
Passing the Current Object
An object can pass itself to another method or constructor using this:
public class Order {
private final List<OrderLine> lines = new ArrayList<>();
public void addLine(String product, int qty) {
lines.add(new OrderLine(this, product, qty)); // the line knows its order
}
}
public class OrderLine {
private final Order order;
private final String product;
private final int qty;
public OrderLine(Order order, String product, int qty) {
this.order = order;
this.product = product;
this.qty = qty;
}
}
Each OrderLine stores a reference back to the Order that created it. The same technique is used when an object registers itself with something else:
public class TemperatureDisplay implements SensorListener {
public TemperatureDisplay(Sensor sensor) {
sensor.addListener(this); // "notify me when readings change"
}
@Override
public void onReading(double value) {
System.out.println("Now: " + value);
}
}
Passing this from inside a constructor (as above) lets other code reach the object before its constructor finishes. If the sensor called onReading immediately, it would run on a partly initialized object. In real code, register listeners after construction, for example in a separate start() method or a factory method.
Returning this for Method Chaining
A method can return this so that calls can be chained:
public class QueryBuilder {
private String table;
private final List<String> conditions = new ArrayList<>();
private int limit = -1;
public QueryBuilder from(String table) {
this.table = table;
return this;
}
public QueryBuilder where(String condition) {
conditions.add(condition);
return this;
}
public QueryBuilder limit(int limit) {
this.limit = limit;
return this;
}
public String build() {
StringBuilder sql = new StringBuilder("SELECT * FROM ").append(table);
if (!conditions.isEmpty()) {
sql.append(" WHERE ").append(String.join(" AND ", conditions));
}
if (limit > 0) {
sql.append(" LIMIT ").append(limit);
}
return sql.toString();
}
}
String sql = new QueryBuilder()
.from("students")
.where("year = 2")
.where("gpa > 8.0")
.limit(10)
.build();
System.out.println(sql);
// SELECT * FROM students WHERE year = 2 AND gpa > 8.0 LIMIT 10
Each method modifies the builder and returns the same object, so the next call operates on it. This is the basis of the builder pattern and of "fluent" APIs. (This example only illustrates chaining; real database code must use PreparedStatement parameters, never strings built from user input, to prevent SQL injection.)
Comparing with this
this can be compared to other references, most commonly as the first check in equals:
@Override
public boolean equals(Object o) {
if (this == o) return true; // same object: equal by definition
if (!(o instanceof Employee other)) return false;
return name.equals(other.name) && salary == other.salary;
}
The this == o check is a quick shortcut: an object is always equal to itself, so there is no need to compare fields.
this Is Not Available in Static Code
Static methods belong to the class, not to any object, so there is no current object:
public class Counter {
private int count;
public static void reset() {
this.count = 0; // compile error: non-static variable this cannot be referenced
// from a static context
}
}
The same applies in main, which is static. This is why early beginner programs fail when main tries to use a field or instance method directly: main has no this.
this in Inner Classes and Lambdas
An inner class (a non-static nested class) has its own this. To reach the enclosing object, qualify it with the outer class name:
public class Course {
private final String title = "Java Basics";
class Lesson {
private final String title;
Lesson(String title) { this.title = title; }
String fullTitle() {
return Course.this.title + " / " + this.title;
}
}
}
this.title is the lesson's title; Course.this.title is the enclosing course's.
A lambda does not introduce a new this. Inside a lambda, this means the same object it means in the surrounding method:
public class Timer {
private final String name = "build-timer";
public Runnable task() {
return () -> System.out.println(this.name); // this is the Timer
}
}
An anonymous class, by contrast, is a separate object, and this inside it refers to the anonymous class instance. This difference is one reason lambdas are simpler to use for short callbacks.
Summary of Uses
| Use | Example | Why |
|---|---|---|
| Access a shadowed field | this.name = name; | Parameter has the same name as the field |
| Call another constructor | this(host, 443); | Share initialization logic |
| Pass the current object | list.add(this); | Give other code a reference to this object |
| Return the current object | return this; | Enable method chaining |
| Compare with another reference | if (this == o) | Identity check, e.g. in equals |
| Reach the enclosing instance | Outer.this.field | From inside an inner class |
Practical Example: Linked Tasks
import java.util.ArrayList;
import java.util.List;
public class Task {
private final String name;
private final List<Task> dependsOn = new ArrayList<>();
private boolean done;
public Task(String name) {
this.name = name;
}
public Task after(Task other) {
if (other == this) {
throw new IllegalArgumentException(name + " cannot depend on itself");
}
this.dependsOn.add(other);
return this;
}
public boolean canStart() {
for (Task t : dependsOn) {
if (!t.done) return false;
}
return true;
}
public void complete() {
if (!this.canStart()) {
throw new IllegalStateException(name + " is blocked");
}
this.done = true;
}
@Override
public String toString() {
return name + (done ? " ✓" : canStart() ? " (ready)" : " (blocked)");
}
public static void main(String[] args) {
Task design = new Task("Design");
Task build = new Task("Build").after(design);
Task test = new Task("Test").after(build);
Task deploy = new Task("Deploy").after(test).after(build);
design.complete();
build.complete();
System.out.println(List.of(design, build, test, deploy));
}
}
Output:
[Design ✓, Build ✓, Test (ready), Deploy (blocked)]
The constructor uses this.name to set the field from a same-named parameter. after uses other == this to reject a self-dependency, adds to this.dependsOn, and returns this so dependencies can be chained in one expression, as in the deploy line. Inside canStart, t.done reads the done field of each dependency, while this.done in complete sets the field of the task the method was called on.