JAVA / CLASSES, CONSTRUCTORS AND ENCAPSULATION
this for disambiguating fields from parameters
Use this.field to reach a field that a same-named parameter or local hides, and recognise the silent bug when you leave this. off.
What you will learn
- Write this.field = field; so the assignment lands on the field, not the parameter
- Explain why a same-named parameter shadows a field for the whole method body
- Spot field = field; which compiles and silently leaves the field at 0 or null
- Use this.level and level in one expression to compare the stored and incoming values
Understanding this for disambiguating fields from parameters
Every instance method and every constructor is handed one extra, invisible argument: a reference to the object it was invoked on, which Java calls this. When you write a bare name such as level, the compiler searches the innermost scope first (the method's parameters and locals) and only looks at the class's fields if nothing there matches. A parameter named level therefore shadows a field named level for the entire method body, not just on the line where the argument arrives.
Writing this.level skips that search: it means the field level on the object this method was called on. That is the whole reason the standard line this.name = name; works, with a field access on the left and the shadowing parameter on the right. Drop the this. and you have name = name;, a perfectly legal assignment of a parameter to itself, so nothing is reported and the field keeps its default null, 0 or false.
It helps to see this as a plain scoping rule rather than a constructor ritual. A local variable declared inside a method shadows a field exactly the same way a parameter does, so the question is never "is this a constructor?" but "is there a nearer variable with this name?". Java code deliberately reuses the field name for the parameter, because that name is part of the readable signature, and pays for it with one this. per assignment; that trade is idiomatic, not sloppy.
public class Main {
static class Point {
private int x;
private int y;
Point(int x, int y) {
x = x; // assigns the parameter to itself; the field stays 0
this.y = y; // assigns the parameter to the field
}
void moveTo(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "(" + x + ", " + y + ")"; // no parameters here, so these are the fields
}
}
public static void main(String[] args) {
Point p = new Point(3, 4);
System.out.println("after constructor: " + p);
p.moveTo(10, 20);
System.out.println("after moveTo: " + p);
}
}
A parameter or local with the same name as a field hides that field inside the method, and this.name is the only way to name the field again.
Worked examples
Shadowing is not only about parameters
A local variable declared inside a method hides a field just as a parameter does.
public class Main {
static class Counter {
private int count = 100;
void addTwice(int count) {
int total = count + count;
this.count = this.count + total;
}
void show() {
int count = -1;
System.out.println("local " + count + ", field " + this.count);
}
}
public static void main(String[] args) {
Counter c = new Counter();
c.show();
c.addTwice(5);
c.show();
}
}
Example explained
Line 1int total = count + count; uses the parameter twice, because inside addTwice the bare name count cannot mean the field.
Line 2this.count = this.count + total; is the only line that touches the object, turning 100 into 110.
Line 3int count = -1; inside show() is legal: the field lives in the class scope, so a local may reuse its name and hide it.
Line 4The printed line puts the local (-1) next to this.count so you can see they are two independent variables.
Comparing the old field with the new value
Shadowing lets a method read the stored value and the incoming value in the same expression.
public class Main {
static class Volume {
private int level = 3;
void setLevel(int level) {
System.out.println("old " + this.level + ", new " + level);
if (level > this.level + 5) {
level = this.level + 5;
System.out.println("clamped to " + level);
}
this.level = level;
}
int level() {
return level;
}
}
public static void main(String[] args) {
Volume v = new Volume();
v.setLevel(6);
v.setLevel(20);
System.out.println("final " + v.level());
}
}
Example explained
Line 1The first println mixes this.level (stored) and level (incoming) in one expression, which works because they are separate variables.
Line 2level = this.level + 5; overwrites the parameter; a parameter is an ordinary local, so reassigning it changes nothing outside the method.
Line 3this.level = level; is the single mutating line, so whatever the clamp left in the parameter becomes the new field value.
Line 4int level() { return level; } needs no this. because no parameter or local named level is in scope there, and a method may share a name with a field.
Important notes
javac says nothing about x = x; there is no -Xlint category for self-assignment, so only an IDE inspection or a tool such as Error Prone will flag it.
this names a receiver object, so it does not exist in static methods or static initialisers; a static factory whose parameter shadows a field must rename the parameter or hand the value to a constructor.
Common mistakes
Writing name = name; in a constructor: it compiles without a word, the field stays null, and the NullPointerException surfaces much later in code that looks unrelated.
Qualifying both sides as this.count = this.count;, which copies the field onto itself and discards the parameter, leaving the same silent default value.
Treating a bare count as the field in a method that has a count parameter, so count++ bumps the parameter and the increment vanishes when the method returns.
Try it yourself
Change, predict, then run
Write a Rectangle class with int width and int height and a constructor Rectangle(int width, int height) where you deliberately leave this. off the height assignment, then print width * height for new Rectangle(3, 4). Add the this. back and confirm the result changes from 0 to 12.
Open the Java workspaceCheck your understanding
Given this class, what does the field hold after b.resize(5)? class Box { private int size = 10; void resize(int size) { size = size + 1; this.size = this.size + size; } }
- 16
- 15
- 22
- 11
Show answer
Both bare occurrences of size mean the parameter, so the first line raises the parameter from 5 to 6 and never touches the field; the second line then computes 10 + 6 = 16. 15 is tempting if you read size = size + 1; as dead code, but a parameter is an ordinary local variable and the later bare read sees its updated 6.