Palavra-chave this em Java
Referencie o objeto atual em Java com a palavra-chave this — desambiguando campos, encadeando construtores e chamadas de métodos.
Dentro de um método de instância ou construtor, this refere-se ao objeto no qual o método está sendo chamado. É um parâmetro implícito que a JVM passa para você. Na maioria das vezes você não precisa mencioná-lo — nomes simples já são resolvidos através de this — mas algumas situações específicas forçam você a escrevê-lo explicitamente. Este capítulo cobre essas situações.
O que é this
Quando você chama dog.bark(), a JVM passa dog secretamente como o primeiro argumento. Dentro de bark, esse argumento é chamado this. Portanto:
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 == rexVocê pode omitir this. e escrever apenas name — Java procura o identificador simples no escopo atual, não encontra nenhuma variável local com esse nome, então percorre até os campos de instância, encontra name e o resolve como this.name. As duas formas compilam para o mesmo bytecode.
Uso 1 — desambiguando um parâmetro de um campo
A razão mais comum para escrever this. explicitamente é quando um parâmetro ou variável local tem o mesmo nome que um campo:
public class Point {
int x, y;
public Point(int x, int y) {
this.x = x; // field = parameter
this.y = y;
}
}Sem this., x = x; atribuiria o parâmetro a si mesmo e deixaria o campo em 0. O compilador não avisa — é uma instrução válida — então esse é um bug silencioso bastante comum.
Você poderia renomear os parâmetros (int newX, int newY) para evitar a colisão, mas fazer os nomes dos parâmetros coincidirem com os nomes dos campos é de longe a convenção mais legível, por isso o padrão this. está em todo lugar no código Java.
Uso 2 — passando o objeto atual
Às vezes um método precisa passar this para outro método ou construtor como argumento:
public class Order {
List<Item> items = new ArrayList<>();
void register(Tracker t) {
t.watch(this); // pass myself to the tracker
}
}Não há outra forma de se referir ao objeto atual como um valor, portanto this é obrigatório aqui.
Uma variante comum é um builder fluente onde cada setter retorna this para que as chamadas possam ser encadeadas:
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 chamar outro construtor
Dentro de um construtor, this(args) chama outro construtor da mesma classe — consulte construtores. Deve ser a primeira instrução, e um construtor só pode delegar para um outro:
public Rectangle() { this(1, 1); }
public Rectangle(double side) { this(side, side); }
public Rectangle(double w, double h){ this.width = w; this.height = h; }Isso é diferente do qualificador this. — this(...) com argumentos é a forma de chamada de construtor; this.foo (ou apenas this sozinho) é a forma de referência.
Quando this é ilegal
this só existe em contextos de instância. Em um método ou inicializador static, não existe objeto atual, portanto escrever this é um erro de compilação:
public class Counter {
static int total;
static void reset() {
this.total = 0; // ERROR: cannot use this in a static context
}
}main é static, por isso você não pode se referir a campos de instância diretamente dentro dele — você deve primeiro criar um objeto.
this vs acesso implícito a campo
Se não houver colisão de nomes, this. é puramente estilístico:
double area() { return Math.PI * radius * radius; }
double areaThisly(){ return Math.PI * this.radius * this.radius; }Ambos funcionam. A forma implícita é mais comum; algumas bases de código usam this. em todo lugar para consistência ou para tornar o receptor visível num relance. Qualquer uma está correta — escolha uma e aplique-a dentro de um arquivo.
Classes internas e o this externo
Uma classe interna não estática tem uma referência implícita à sua instância envolvente. Dentro da classe interna, this refere-se ao objeto interno; a instância externa é 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
}
}
}Isso surge apenas em cenários de classe aninhada, abordados em classes aninhadas e nos capítulos seguintes.
Lambdas vs classes anônimas
Um lambda não introduz um novo this. Dentro de um lambda, this é a instância envolvente — o mesmo objeto no qual o método circundante é executado. Uma classe anônima, por outro lado, é seu próprio objeto, portanto dentro dela this refere-se a essa instância 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
}
}Essa é uma das poucas diferenças comportamentais entre as duas formas: um lambda captura this de seu entorno, enquanto uma classe anônima o oculta.
Um exemplo prático
O que vem a seguir
this é uma das duas referências implícitas em Java; a outra, super, surge quando você tem herança. Antes disso, porém, precisamos falar sobre visibilidade — quem pode ver e chamar os métodos e campos da sua classe. Continue para modificadores de acesso.