La palabra clave this en Java
Referencia al objeto actual en Java con this: desambiguación de campos, encadenamiento de constructores y llamadas a métodos.
Dentro de un método de instancia o constructor, this hace referencia a el objeto sobre el que se está llamando al método. Es un parámetro implícito que la JVM te pasa automáticamente. La mayor parte del tiempo no necesitas mencionarlo — los nombres simples ya se resuelven a través de this — pero hay un puñado de situaciones que te obligan a escribirlo explícitamente. Este capítulo las cubre.
Qué es this
Cuando llamas a dog.bark(), la JVM pasa dog en secreto como primer argumento. Dentro de bark, ese argumento se llama this. Así:
public class Dog {
String name;
void bark() {
System.out.println(this.name + " says woof"); // this == dog
}
}
Dog rex = new Dog();
rex.name = "Rex";
rex.bark(); // inside, this == rexPuedes omitir this. y escribir simplemente name — Java busca el identificador simple en el ámbito actual, no encuentra ninguna variable local con ese nombre, luego sube a los campos de instancia, encuentra name y lo resuelve como this.name. Las dos formas compilan al mismo bytecode.
Uso 1 — desambiguar un parámetro de un campo
La razón más común para escribir this. explícitamente es cuando un parámetro o variable local tiene el mismo nombre que un campo:
public class Point {
int x, y;
public Point(int x, int y) {
this.x = x; // field = parameter
this.y = y;
}
}Sin this., x = x; asignaría el parámetro a sí mismo y dejaría el campo en 0. El compilador no advierte — es una instrucción válida — por lo que este es un error silencioso muy popular.
Podrías renombrar los parámetros (int newX, int newY) para evitar la colisión, pero hacer que los nombres de los parámetros coincidan con los nombres de los campos es con diferencia la convención más legible y, por eso, el patrón this. está en todas partes en el código Java.
Uso 2 — pasar el objeto actual
A veces un método necesita pasar this a otro método o constructor como argumento:
public class Order {
List<Item> items = new ArrayList<>();
void register(Tracker t) {
t.watch(this); // pass myself to the tracker
}
}No hay otra forma de referirse al objeto actual como un valor, por lo que this es obligatorio aquí.
Una variante común es un builder fluido donde cada setter devuelve this para que las llamadas puedan encadenarse:
public class Url {
String scheme, host, path;
public Url scheme(String s) { this.scheme = s; return this; }
public Url host(String h) { this.host = h; return this; }
public Url path(String p) { this.path = p; return this; }
}
Url u = new Url().scheme("https").host("w3docs.com").path("/learn-java");Uso 3 — this(...) para llamar a otro constructor
Dentro de un constructor, this(args) llama a otro constructor de la misma clase — consulta constructores. Debe ser la primera instrucción y un constructor solo puede delegar en otro:
public Rectangle() { this(1, 1); }
public Rectangle(double side) { this(side, side); }
public Rectangle(double w, double h){ this.width = w; this.height = h; }Esto es diferente del calificador this. — this(...) con argumentos es la forma de llamada al constructor; this.foo (o simplemente this solo) es la forma de referencia.
Cuándo this es ilegal
this solo existe en contextos de instancia. Desde un método static o inicializador, no hay objeto actual, por lo que escribir this es un error de compilación:
public class Counter {
static int total;
static void reset() {
this.total = 0; // ERROR: cannot use this in a static context
}
}main es static, razón por la cual no puedes referirte directamente a campos de instancia dentro de él — primero debes crear un objeto.
this frente al acceso implícito a campos
Si no hay colisión de nombres, this. es puramente estilístico:
double area() { return Math.PI * radius * radius; }
double areaThisly(){ return Math.PI * this.radius * this.radius; }Ambas funcionan. La forma implícita es más común; algunas bases de código usan this. en todas partes por consistencia o para que el receptor sea visible de un vistazo. Cualquiera está bien — elige uno y aplícalo dentro de un archivo.
Clases internas y el this externo
Una clase interna no estática tiene una referencia implícita a su instancia envolvente. Desde la clase interna, this hace referencia al objeto interno; la instancia externa es Outer.this:
public class Outer {
int x = 1;
class Inner {
int x = 2;
void demo() {
System.out.println(this.x); // 2 — Inner's x
System.out.println(Outer.this.x); // 1 — Outer's x
}
}
}Esto aparece solo en escenarios de clases anidadas, cubiertos en clases anidadas y los capítulos siguientes.
Lambdas frente a clases anónimas
Una lambda no introduce un nuevo this. Dentro de una lambda, this es la instancia envolvente — el mismo objeto sobre el que se ejecuta el método circundante. Una clase anónima, en cambio, es su propio objeto, por lo que dentro de ella this hace referencia a esa instancia anónima:
public class Demo {
String label = "outer";
void run() {
Runnable lambda = () -> System.out.println(this.label); // "outer"
Runnable anon = new Runnable() {
String label = "inner";
public void run() { System.out.println(this.label); } // "inner"
};
lambda.run(); // prints outer
anon.run(); // prints inner
}
}Esta es una de las pocas diferencias de comportamiento entre las dos formas: una lambda captura this de su entorno, mientras que una clase anónima lo oculta.
Un ejemplo completo
Qué sigue
this es una de las dos referencias implícitas en Java; la otra, super, aparece una vez que tienes herencia. Antes de eso, sin embargo, necesitamos hablar de visibilidad — quién puede ver y llamar a los métodos y campos de tu clase. Continúa con modificadores de acceso.