La parola chiave this in Java
Fai riferimento all'oggetto corrente in Java con this: disambigua campi, concatena costruttori e chiama metodi.
All'interno di un metodo di istanza o di un costruttore, this fa riferimento a l'oggetto su cui viene chiamato il metodo. È un parametro implicito che la JVM passa automaticamente. La maggior parte delle volte non è necessario menzionarlo — i nomi semplici vengono già risolti tramite this — ma ci sono alcune situazioni che richiedono di scriverlo esplicitamente. Questo capitolo le illustra.
Cosa rappresenta this
Quando chiami dog.bark(), la JVM passa segretamente dog come primo argomento. All'interno di bark, quell'argomento si chiama this. Quindi:
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 == rexPuoi omettere this. e scrivere semplicemente name — Java cerca l'identificatore nel contesto corrente, non trova nessuna variabile locale con quel nome, poi si sposta ai campi dell'istanza, trova name e lo risolve come this.name. Le due forme compilano nello stesso bytecode.
Uso 1 — disambiguare un parametro da un campo
Il motivo più comune per scrivere this. esplicitamente è quando un parametro o una variabile locale ha lo stesso nome di un campo:
public class Point {
int x, y;
public Point(int x, int y) {
this.x = x; // field = parameter
this.y = y;
}
}Senza this., x = x; assegnerebbe il parametro a sé stesso e lascerebbe il campo a 0. Il compilatore non avverte — è un'istruzione valida — quindi questo è un bug silenzioso molto diffuso.
Potresti rinominare i parametri (int newX, int newY) per evitare la collisione, ma far corrispondere i nomi dei parametri ai nomi dei campi è di gran lunga la convenzione più leggibile, quindi il pattern con this. è onnipresente nel codice Java.
Uso 2 — passare l'oggetto corrente
A volte un metodo deve passare this a un altro metodo o costruttore come argomento:
public class Order {
List<Item> items = new ArrayList<>();
void register(Tracker t) {
t.watch(this); // pass myself to the tracker
}
}Non esiste altro modo per fare riferimento all'oggetto corrente come valore, quindi this è obbligatorio qui.
Una variante comune è un builder fluente in cui ogni setter restituisce this in modo che le chiamate possano essere concatenate:
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(...) per chiamare un altro costruttore
All'interno di un costruttore, this(args) chiama un altro costruttore della stessa classe — vedi costruttori. Deve essere la prima istruzione e un costruttore può delegare solo a un altro:
public Rectangle() { this(1, 1); }
public Rectangle(double side) { this(side, side); }
public Rectangle(double w, double h){ this.width = w; this.height = h; }Questa forma è diversa dal qualificatore this. — this(...) con argomenti è la forma di chiamata del costruttore; this.foo (o semplicemente this da solo) è la forma di riferimento.
Quando this non è consentito
this esiste solo nei contesti di istanza. Da un metodo o inizializzatore static, non esiste un oggetto corrente, quindi scrivere this è un errore di compilazione:
public class Counter {
static int total;
static void reset() {
this.total = 0; // ERROR: cannot use this in a static context
}
}main è static, motivo per cui non puoi fare riferimento direttamente ai campi di istanza al suo interno — devi prima creare un oggetto.
this vs accesso implicito ai campi
Se non c'è collisione di nomi, this. è puramente stilistico:
double area() { return Math.PI * radius * radius; }
double areaThisly(){ return Math.PI * this.radius * this.radius; }Entrambe funzionano. La forma implicita è più comune; alcuni codebase usano this. ovunque per coerenza o per rendere visibile il ricevitore a colpo d'occhio. Entrambe vanno bene — sceglierne una e applicarla uniformemente all'interno di un file.
Classi interne e il this esterno
Una classe interna non statica ha un riferimento implicito alla sua istanza di inclusione. Dalla classe interna, this fa riferimento all'oggetto interno; l'istanza esterna è 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
}
}
}Questo si verifica solo in scenari con classi nidificate, trattati in classi nidificate e nei capitoli successivi.
Lambda vs classi anonime
Una lambda non introduce un nuovo this. All'interno di una lambda, this è l'istanza di inclusione — lo stesso oggetto su cui viene eseguito il metodo circostante. Una classe anonima, al contrario, è un oggetto a sé, quindi al suo interno this fa riferimento a quell'istanza anonima:
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
}
}Questa è una delle poche differenze comportamentali tra le due forme: una lambda cattura this dal contesto circostante, mentre una classe anonima lo oscura.
Un esempio pratico
Cosa c'è dopo
this è uno dei due riferimenti impliciti in Java; l'altro, super, entra in gioco una volta introdotta l'ereditarietà. Prima però dobbiamo parlare di visibilità — chi può vedere e chiamare i metodi e i campi della tua classe. Continua con i modificatori di accesso.