Java this
this ("este", em inglês) é uma palavra do Java que significa "o objeto em que este código está rodando agora". Ela aparece o tempo todo em código profissional, quase sempre na forma this.nome = nome;. Nesta aula você entende por que ela existe, o bug clássico de quem a esquece e os outros usos dela.
- O bug clássico:
nome = nome;no construtor, que compila e não faz nada. - Como
this.nomediferencia o atributo do parâmetro de mesmo nome. - Que
thisé uma referência de verdade ao objeto atual: dá para imprimir, comparar, passar e devolver. - O
this(…), que chama outro construtor. - Por que não existe
thisem métodosstatic.
A ideia antes do código: "eu"
Quando a Ana diz "meu nome é Ana", a palavra "meu" se refere a ela. Quando o Bruno diz "meu nome é Bruno", a mesma palavra se refere a ele. "Meu" não é o nome de ninguém: é um jeito de cada um apontar para si mesmo.
O this é o "eu" de cada objeto. O código de um método é escrito uma vez, na classe, mas roda em objetos diferentes. Dentro dele, this aponta para o objeto que recebeu a chamada: em maca.mostrar(), this é a maçã; em uva.mostrar(), é a uva.
O bug clássico: nome = nome
Na aula de Construtores, os parâmetros tinham nomes como nomeInicial. Isso é chato: o nome bom, nome, já está ocupado pelo atributo. A Ana resolveu usar o mesmo nome nos dois:
class Fruta {
String nome;
double preco;
Fruta(String nome, double preco) {
nome = nome;
preco = preco;
}
}
public class Main {
public static void main(String[] args) {
Fruta maca = new Fruta("maçã", 7.9);
System.out.println(maca.nome + ": R$ " + maca.preco);
}
}
$ javac Main.java && java Main null: R$ 0.0 « a maçã ficou sem nome e sem preço!
Compila sem nenhum aviso (nem com javac -Xlint:all), roda sem erro e o resultado está errado. O que aconteceu? Lembre do sombreamento: dentro do construtor existem dois nome, o atributo do objeto e o parâmetro. O mais "próximo", o parâmetro, faz sombra no atributo. Então a linha nome = nome; quer dizer "parâmetro nome recebe o parâmetro nome": copia o valor para ele mesmo. O atributo nunca é tocado e fica com o valor padrão, null.
| Dentro do construtor | Quem é | Valor |
|---|---|---|
nome | O parâmetro (faz sombra no atributo) | "maçã" |
this.nome | O atributo do objeto que está nascendo | null, até alguém atribuir |
A correção: this.nome = nome
class Fruta {
String nome;
double preco;
Fruta(String nome, double preco) {
this.nome = nome;
this.preco = preco;
}
void mudarPreco(double preco) {
this.preco = preco;
}
}
public class Main {
public static void main(String[] args) {
Fruta maca = new Fruta("maçã", 7.9);
System.out.println(maca.nome + ": R$ " + maca.preco);
maca.mudarPreco(8.5);
System.out.println(maca.nome + ": R$ " + maca.preco);
}
}
$ javac Main.java && java Main maçã: R$ 7.9 maçã: R$ 8.5
- this
- O objeto atual: o que está sendo criado (no construtor) ou o que recebeu a chamada (num método).
- .nome
- O atributo
nomedesse objeto.this.nomeé comomaca.nome, só que de dentro da classe. - = nome;
- Sem
this,nomeé o parâmetro. A linha lê-se: "o meu nome recebe o nome que eu recebi". - mudarPreco(double preco)
- O mesmo padrão vale para métodos comuns: sempre que um parâmetro tiver o nome de um atributo, use
this.para chegar ao atributo.
Esse é o padrão de praticamente todo código Java: parâmetros com o mesmo nome dos atributos, e this.atributo = parametro;. As IDEs geram construtores assim automaticamente.
Missão: o construtor da Conta tem o bug clássico e a conta da Ana sai como null: R$ 0.0. Corrija as duas linhas com this para aparecer Ana: R$ 500.0.
class Conta {
String titular;
double saldo;
Conta(String titular, double saldo) {
titular = titular;
saldo = saldo;
}
}
public class Main {
public static void main(String[] args) {
Conta c = new Conta("Ana", 500);
System.out.println(c.titular + ": R$ " + c.saldo);
}
}
class Conta {
String titular;
double saldo;
Conta(String titular, double saldo) {
this.titular = titular;
this.saldo = saldo;
}
}
public class Main {
public static void main(String[] args) {
Conta c = new Conta("Ana", 500);
System.out.println(c.titular + ": R$ " + c.saldo);
}
}
this é uma referência de verdade
this não é só um prefixo para atributos: é uma referência ao objeto atual, igual às variáveis que você viu em Classes e Objetos. Dá para imprimi-la e compará-la:
class Fruta {
String nome;
double preco;
Fruta(String nome, double preco) {
this.nome = nome;
this.preco = preco;
}
void quemSouEu() {
System.out.println("this = " + this);
}
boolean maisCaraQue(Fruta outra) {
return this.preco > outra.preco;
}
}
public class Main {
public static void main(String[] args) {
Fruta maca = new Fruta("maçã", 7.9);
Fruta uva = new Fruta("uva", 12.0);
System.out.println("maca = " + maca);
maca.quemSouEu();
System.out.println("uva = " + uva);
uva.quemSouEu();
System.out.println("uva mais cara que maçã? " + uva.maisCaraQue(maca));
System.out.println("maçã mais cara que uva? " + maca.maisCaraQue(uva));
}
}
$ javac Main.java && java Main maca = Fruta@1dbd16a6 this = Fruta@1dbd16a6 « dentro de maca.quemSouEu(), this É a maçã uva = Fruta@251a69d7 this = Fruta@251a69d7 « dentro de uva.quemSouEu(), this é a uva uva mais cara que maçã? true maçã mais cara que uva? false
O identificador depois do @ é o mesmo para maca e para o this da chamada maca.quemSouEu(): são o mesmo objeto. No maisCaraQue, o this.preco não era obrigatório (não há sombra), mas deixa a comparação mais legível: "o meu preço é maior que o preço da outra?".
Passar this para outro objeto
Um objeto pode se "apresentar" a outro passando this como argumento. Na escola da Ana, o aluno se matricula numa turma, e a turma precisa conhecer o aluno:
class Aluno {
String nome;
Turma turma;
Aluno(String nome) {
this.nome = nome;
}
void matricular(Turma turma) {
this.turma = turma;
turma.receber(this);
}
}
class Turma {
String codigo;
int quantidade;
Turma(String codigo) {
this.codigo = codigo;
}
void receber(Aluno aluno) {
quantidade++;
System.out.println(aluno.nome + " entrou na turma " + codigo + " (" + quantidade + " aluno(s))");
}
}
public class Main {
public static void main(String[] args) {
Turma t = new Turma("1A");
new Aluno("Bruno").matricular(t);
new Aluno("Carla").matricular(t);
}
}
$ javac Main.java && java Main Bruno entrou na turma 1A (1 aluno(s)) Carla entrou na turma 1A (2 aluno(s))
Em matricular, this.turma = turma; guarda a turma no aluno, e turma.receber(this); entrega à turma a referência do próprio aluno ("sou eu que estou entrando"). Repare também em new Aluno("Bruno").matricular(t): dá para chamar um método direto no objeto recém-criado.
Devolver this: chamadas em cadeia
Se um método devolve this, quem chamou recebe de volta o mesmo objeto e pode chamar outro método em seguida, na mesma linha:
class Pedido {
String itens = "";
double total = 0;
Pedido adicionar(String fruta, double preco) {
this.itens = this.itens + fruta + " ";
this.total = this.total + preco;
return this;
}
void fechar() {
System.out.println("Itens: " + itens);
System.out.println("Total: R$ " + total);
}
}
public class Main {
public static void main(String[] args) {
Pedido pedido = new Pedido();
pedido.adicionar("maçã", 7.9).adicionar("uva", 12.0).adicionar("kiwi", 2.5);
pedido.fechar();
}
}
$ javac Main.java && java Main Itens: maçã uva kiwi Total: R$ 22.4
O tipo de retorno de adicionar é a própria classe, Pedido. pedido.adicionar("maçã", 7.9) devolve o pedido; nele, chama-se .adicionar("uva", 12.0), que devolve o pedido de novo, e assim por diante. Esse estilo se chama interface fluente e você vai encontrá-lo no StringBuilder (sb.append("a").append("b")) e nos Streams.
this(…) no construtor
Com parênteses logo depois, this(…) não é o objeto: é a chamada de outro construtor da mesma classe. Você já viu em Construtores. Junto com o this.campo, fica assim:
class Fruta {
String nome;
double preco;
Fruta(String nome) {
this(nome, 0.0); // chama o construtor de baixo
}
Fruta(String nome, double preco) {
this.nome = nome; // atributo = parâmetro
this.preco = preco;
}
}
| Forma | O que é | Onde pode aparecer |
|---|---|---|
this.nome | Atributo do objeto atual | Construtores e métodos de instância |
this.metodo() | Método do objeto atual (o this. é opcional) | Construtores e métodos de instância |
this sozinho | Referência ao objeto atual (imprimir, comparar, passar, devolver) | Construtores e métodos de instância |
this(…) | Chamada a outro construtor da mesma classe | Só dentro de construtores |
Quando usar this.?
| Situação | this. é… |
|---|---|
| Parâmetro ou variável local com o mesmo nome do atributo | Obrigatório para chegar ao atributo |
| Sem sombra (nomes diferentes) | Opcional: preco e this.preco são a mesma coisa |
Comparar com outro objeto do mesmo tipo (this.preco > outra.preco) | Opcional, mas deixa a leitura mais clara |
Métodos static | Proibido: não há objeto atual |
Algumas equipes escrevem this. sempre, para deixar claro o que é atributo; outras só quando é obrigatório. As duas formas estão certas. O importante é nunca esquecer nos construtores e setters (veja Encapsulamento).
Quando dá errado
null / 0 depois do construtor
$ java Main.java null: R$ 0.0
O que aconteceu: o bug clássico: nome = nome; com parâmetro e atributo de mesmo nome. O parâmetro faz sombra, e a linha copia o parâmetro para ele mesmo. Nenhuma mensagem de erro ou aviso.
Como resolver: this.nome = nome;. Sempre que vir x = x;, desconfie.
public class Main {
String saudacao = "Olá";
public static void main(String[] args) {
System.out.println(this.saudacao);
}
}
$ javac Main.java
Main.java:5: error: non-static variable this cannot be referenced from a static context
System.out.println(this.saudacao);
^
1 error
O que aconteceu: o main é static: roda sem nenhum objeto. Não existe "objeto atual", logo não existe this.
Como resolver: crie um objeto e use a variável dele (Main m = new Main(); m.saudacao), ou torne o atributo static se ele for da classe. Veja static.
O que aconteceu: o this(…) chamou o próprio construtor, ou passou argumentos que nenhum construtor aceita.
Como resolver: veja os cenários em Construtores.
Referência
| Escrita | Significado |
|---|---|
this.campo = campo; | Atributo recebe o parâmetro de mesmo nome (o uso mais comum) |
this | Referência ao objeto atual |
outro.receber(this); | Passa o objeto atual para outro objeto |
return this; | Devolve o objeto atual (chamadas em cadeia) |
this(args); | Chama outro construtor da mesma classe |
this em static | Erro: non-static variable this cannot be referenced from a static context |
Missão final: a conta fluente
Missão: faça depositar e sacar devolverem a própria conta (tipo de retorno Conta e return this;) para que a linha em cadeia do main funcione e o programa mostre Saldo: 120.0.
class Conta {
double saldo;
void depositar(double valor) {
this.saldo = this.saldo + valor;
}
void sacar(double valor) {
this.saldo = this.saldo - valor;
}
}
public class Main {
public static void main(String[] args) {
Conta c = new Conta();
c.depositar(100).depositar(50).sacar(30);
System.out.println("Saldo: " + c.saldo);
}
}
class Conta {
double saldo;
Conta depositar(double valor) {
this.saldo = this.saldo + valor;
return this;
}
Conta sacar(double valor) {
this.saldo = this.saldo - valor;
return this;
}
}
public class Main {
public static void main(String[] args) {
Conta c = new Conta();
c.depositar(100).depositar(50).sacar(30);
System.out.println("Saldo: " + c.saldo);
}
}
Exercícios
1. Corrigir o construtor
Complete a linha do construtor Aluno(String nome) que guarda o parâmetro no atributo nome:
2. Devolver o objeto
Complete a última linha de um método que permite chamadas em cadeia:
3. Chamar outro construtor
Complete o construtor Fruta() para chamar Fruta(String nome, double preco) com "sem nome" e 0.0:
4. No construtor Conta(double saldo) { saldo = saldo; }, o que acontece com o atributo saldo?
5. Em uva.mostrar(), dentro de mostrar, o que é this?
6. Onde this não pode ser usado?
Resumo
thisé a referência ao objeto atual: o que está sendo construído ou o que recebeu a chamada.- Parâmetro com o mesmo nome do atributo faz sombra;
nome = nome;não muda nada. Usethis.nome = nome;. thispode ser impresso, comparado, passado como argumento (turma.receber(this)) e devolvido (return this;, para chamadas em cadeia).this(…)chama outro construtor da mesma classe.- Em métodos
staticnão há objeto atual:thisnão existe ali.