Java Métodos de Objeto
Na aula de Atributos, o main mexia direto no saldo de cada conta: daAna.saldo = daAna.saldo + 500. Funciona, mas a lógica fica espalhada pelo programa. O jeito orientado a objetos é dar à própria conta o comportamento de depositar: daAna.depositar(500). Esses são os métodos de objeto (ou métodos de instância), os métodos sem static.
- Como escrever um método de instância e por que ele não leva
static. - Como ele usa os atributos do próprio objeto, sem ponto.
- Como chamar com
objeto.metodo()e como o Java sabe "de qual objeto" é cada atributo. - Métodos que devolvem valores, que chamam outros métodos do mesmo objeto e que recebem outros objetos.
- A diferença entre métodos
statice de instância, e o erronon-static method … cannot be referenced from a static context.
A ideia antes do código: pedir em vez de fazer
No banco, você não entra no cofre para pôr dinheiro na sua conta: você pede ao caixa "deposite 500 na conta da Ana". Quem sabe como fazer um depósito é o banco. Em POO, é igual: em vez de o main mexer nos dados de cada objeto, ele pede ao objeto que faça algo, e o objeto sabe como mexer nos próprios dados.
| Mexendo direto (aula anterior) | Pedindo ao objeto (esta aula) |
|---|---|
daAna.saldo = daAna.saldo + 500; | daAna.depositar(500); |
| Cada lugar do programa repete a conta | A regra do depósito está escrita uma vez, dentro da classe |
Fácil errar (e se alguém escrever - em vez de +?) | Quem chama só diz o que quer, não como |
Seu primeiro método de objeto
class Conta {
String titular;
double saldo;
void depositar(double valor) {
saldo = saldo + valor;
}
void mostrarSaldo() {
System.out.println(titular + " tem R$ " + saldo);
}
}
public class Main {
public static void main(String[] args) {
Conta daAna = new Conta();
daAna.titular = "Ana";
Conta doBruno = new Conta();
doBruno.titular = "Bruno";
daAna.depositar(500);
doBruno.depositar(80);
daAna.depositar(20);
daAna.mostrarSaldo();
doBruno.mostrarSaldo();
}
}
$ javac Main.java && java Main Ana tem R$ 520.0 Bruno tem R$ 80.0
- void depositar(double valor)
- Um método como os que você já conhece, com parâmetro e tipo de retorno, mas sem
static. Isso faz dele um método de instância: ele pertence a cada objeto, e não à classe. - saldo = saldo + valor;
- Usa o atributo
saldodireto pelo nome, sem ponto. Qual saldo? O do objeto que recebeu o pedido. - void mostrarSaldo()
- Outro método de instância, que lê dois atributos (
titularesaldo) do próprio objeto. - daAna.depositar(500);
- A chamada: objeto, ponto, método e argumentos. Lê-se "conta da Ana, deposite 500".
Como o método sabe de qual objeto é o saldo?
O mesmo código, saldo = saldo + valor;, mexeu no saldo da Ana numa chamada e no do Bruno na outra. O segredo está no que vem antes do ponto. Quando você escreve daAna.depositar(500), o Java executa o método "dentro" do objeto daAna: todo atributo citado no corpo é o atributo desse objeto.
| Chamada | Dentro do método, saldo é… | Saldo depois |
|---|---|---|
daAna.depositar(500) | o saldo da Ana | Ana: 500.0 |
doBruno.depositar(80) | o saldo do Bruno | Bruno: 80.0 |
daAna.depositar(20) | o saldo da Ana de novo | Ana: 520.0 |
Por baixo dos panos, todo método de instância recebe uma referência escondida para o objeto que foi chamado. Ela tem nome: this ("este"). saldo dentro do método é, na verdade, this.saldo. A aula this mostra quando é preciso escrever isso por extenso.
Missão: acrescente à classe Conta um método void sacar(double valor) que tire o valor do saldo. No main, faça a Ana sacar 120 antes do mostrarSaldo(), para aparecer Ana tem R$ 400.0.
class Conta {
String titular;
double saldo;
void depositar(double valor) {
saldo = saldo + valor;
}
void mostrarSaldo() {
System.out.println(titular + " tem R$ " + saldo);
}
}
public class Main {
public static void main(String[] args) {
Conta daAna = new Conta();
daAna.titular = "Ana";
daAna.depositar(520);
daAna.mostrarSaldo();
}
}
class Conta {
String titular;
double saldo;
void depositar(double valor) {
saldo = saldo + valor;
}
void sacar(double valor) {
saldo = saldo - valor;
}
void mostrarSaldo() {
System.out.println(titular + " tem R$ " + saldo);
}
}
public class Main {
public static void main(String[] args) {
Conta daAna = new Conta();
daAna.titular = "Ana";
daAna.depositar(520);
daAna.sacar(120);
daAna.mostrarSaldo();
}
}
Métodos que devolvem, decidem e conversam
Tudo o que você aprendeu sobre métodos vale aqui: eles podem devolver valores, ter if, chamar outros métodos. Veja uma conta mais completa:
class Conta {
String titular;
double saldo;
void depositar(double valor) {
saldo = saldo + valor;
}
boolean sacar(double valor) {
if (valor > saldo) {
System.out.println("Saque de " + valor + " negado: saldo insuficiente.");
return false;
}
saldo = saldo - valor;
return true;
}
void transferir(Conta destino, double valor) {
if (sacar(valor)) {
destino.depositar(valor);
}
}
String resumo() {
return titular + ": R$ " + saldo;
}
}
public class Main {
public static void main(String[] args) {
Conta daAna = new Conta();
daAna.titular = "Ana";
daAna.depositar(500);
Conta doBruno = new Conta();
doBruno.titular = "Bruno";
boolean deuCerto = daAna.sacar(120);
System.out.println("Primeiro saque: " + deuCerto);
System.out.println("Segundo saque: " + daAna.sacar(1000));
daAna.transferir(doBruno, 50);
System.out.println(daAna.resumo());
System.out.println(doBruno.resumo());
}
}
$ javac Main.java && java Main Primeiro saque: true Saque de 1000.0 negado: saldo insuficiente. Segundo saque: false Ana: R$ 330.0 « 500 - 120 - 50 Bruno: R$ 50.0 « recebeu a transferência
- boolean sacar(double valor)
- Devolve
truese o saque deu certo efalsese não havia saldo. A regra ("não pode sacar mais do que tem") agora mora na classe: nenhum lugar do programa consegue esquecer de conferir. - if (sacar(valor))
- Dentro de
transferir, a chamadasacar(valor)não tem objeto antes: é o mesmo objeto que está transferindo. EmdaAna.transferir(…), quem saca é a Ana. - Conta destino
- Um parâmetro cujo tipo é a própria classe. O método recebe a referência de outra conta…
- destino.depositar(valor);
- …e pede a ela que deposite. Aqui o ponto é necessário: é outro objeto.
- String resumo()
- Em vez de imprimir, devolve um texto pronto; quem chama decide o que fazer com ele.
| Dentro de um método de instância, você escreve… | …e isso significa |
|---|---|
saldo | o atributo do objeto que foi chamado |
sacar(10) | chamar outro método do mesmo objeto |
destino.saldo, destino.depositar(10) | o atributo ou método de outro objeto |
static × de instância
Agora você conhece os dois tipos de método. A diferença é a pergunta "precisa de um objeto?":
Método static (da classe) | Método de instância (do objeto) | |
|---|---|---|
| Declaração | static double media(double a, double b) | double media() |
| Chamada | Main.media(7, 8) ou só media(7, 8) dentro da classe | aluno.media(): precisa de um objeto antes do ponto |
| Usa atributos de instância? | Não pode: não há "objeto atual" | Sim, direto pelo nome |
| De onde vêm os dados? | Só dos parâmetros | Dos atributos do objeto (e dos parâmetros) |
| Exemplos do Java | Math.sqrt(2), Math.max(3, 7), Integer.parseInt("42") | texto.length(), texto.toUpperCase(), teclado.nextInt() |
Repare nos exemplos: Math.sqrt(2) não precisa de "um Math"; a raiz de 2 é a mesma para todo mundo. Já texto.length() depende de qual texto. Use essa pergunta para decidir: o resultado depende dos dados de um objeto? Então é de instância. Na aula static você aprofunda o assunto.
main?
O main é static: o Java o chama antes de existir qualquer objeto. É por isso que, nas aulas de Métodos, tudo precisava de static. A partir de agora, o main vira o lugar que cria os objetos e pede coisas a eles; a lógica fica nos métodos de instância das classes.
Quando dá errado
class Conta {
String titular;
double saldo;
void depositar(double valor) {
saldo = saldo + valor;
}
}
public class Main {
public static void main(String[] args) {
Conta.depositar(100);
}
}
$ javac Main.java
Main.java:12: error: non-static method depositar(double) cannot be referenced from a static context
Conta.depositar(100);
^
1 error
O que aconteceu: "o método não estático depositar(double) não pode ser usado a partir de um contexto estático". Conta.depositar(100) pede um depósito à classe (o molde), e não a uma conta. Depositar em qual conta? Não há objeto para receber o dinheiro.
Como resolver: crie (ou use) um objeto e chame por ele: Conta c = new Conta(); c.depositar(100);. O mesmo erro aparece quando você escreve um método sem static na classe Main e o chama do main (veja Métodos).
$ javac Main.java
Main.java:12: error: cannot find symbol
depositar(100);
^
symbol: method depositar(int)
location: class Main
1 error
O que aconteceu: sem nada antes, depositar(100) procura um método depositar na classe atual, Main. Ele existe, mas na classe Conta.
Como resolver: minhaConta.depositar(100);, com o objeto antes do ponto.
$ javac Main.java
Main.java:13: error: cannot find symbol
conta.sacar(100);
^
symbol: method sacar(int)
location: variable conta of type Conta
1 error
O que aconteceu: o objeto está certo, mas a classe Conta não tem um método sacar que aceite um int. Ou o método não foi escrito, ou o nome está diferente.
Como resolver: confira o nome e os parâmetros na classe. Se o método não existe, escreva-o.
$ javac Main.java
Main.java:6: error: non-static variable saldo cannot be referenced from a static context
saldo = saldo + valor;
^
Main.java:6: error: non-static variable saldo cannot be referenced from a static context
saldo = saldo + valor;
^
2 errors
O que aconteceu: o método foi declarado como static void depositar(…) (o hábito das primeiras aulas!). Um método static não tem objeto atual, então não sabe de quem é o saldo.
Como resolver: tire o static dos métodos que usam atributos do objeto.
NullPointerException: Cannot invoke …
$ javac Main.java && java Main Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Conta.depositar(double)" because "<local1>" is null at Main.main(Main.java:13)
O que aconteceu: "não é possível chamar Conta.depositar(double) porque a variável é nula". A variável conta valia null: não havia objeto para receber o pedido.
Como resolver: garanta um new Conta() antes de chamar métodos. Veja null.
Referência
| Escrita | Significado |
|---|---|
Tipo nome(params) { … } (sem static, na classe) | Método de instância |
obj.metodo(args) | Chama o método no objeto obj |
atributo dentro do método | O atributo do objeto que foi chamado (o mesmo que this.atributo) |
outroMetodo() dentro do método | Chama outro método do mesmo objeto |
void f(Classe outro) | Recebe a referência de outro objeto; use outro.algo |
Classe.metodo() | Só para métodos static |
Missão final: o aluno que sabe a própria situação
Missão: na classe Aluno, escreva dois métodos de instância: double media(), que devolve a média de nota1 e nota2, e String situacao(), que devolve "aprovado" se a media() for 7 ou mais e "recuperação" se não. O main já está pronto.
class Aluno {
String nome;
double nota1;
double nota2;
// escreva media() e situacao() aqui
}
public class Main {
public static void main(String[] args) {
Aluno bruno = new Aluno();
bruno.nome = "Bruno";
bruno.nota1 = 8.0;
bruno.nota2 = 7.0;
Aluno davi = new Aluno();
davi.nome = "Davi";
davi.nota1 = 5.0;
davi.nota2 = 6.0;
System.out.println(bruno.nome + ": " + bruno.media() + " (" + bruno.situacao() + ")");
System.out.println(davi.nome + ": " + davi.media() + " (" + davi.situacao() + ")");
}
}
class Aluno {
String nome;
double nota1;
double nota2;
double media() {
return (nota1 + nota2) / 2;
}
String situacao() {
if (media() >= 7) {
return "aprovado";
}
return "recuperação";
}
}
public class Main {
public static void main(String[] args) {
Aluno bruno = new Aluno();
bruno.nome = "Bruno";
bruno.nota1 = 8.0;
bruno.nota2 = 7.0;
Aluno davi = new Aluno();
davi.nome = "Davi";
davi.nota1 = 5.0;
davi.nota2 = 6.0;
System.out.println(bruno.nome + ": " + bruno.media() + " (" + bruno.situacao() + ")");
System.out.println(davi.nome + ": " + davi.media() + " (" + davi.situacao() + ")");
}
}
Exercícios
1. Chamar no objeto
Complete para pedir à conta poupanca que deposite 200:
2. A palavra que sai
Um método que usa atributos do objeto não pode ter qual palavra na declaração?
3. Usar o atributo
Complete o corpo do método da classe Fruta (que tem o atributo double preco) para dar um desconto de 10%:
4. Em daAna.transferir(doBruno, 50), dentro do método, a chamada sacar(50) (sem nada antes) saca de qual conta?
5. Qual destes faz mais sentido como método static?
6. O que significa non-static method depositar(double) cannot be referenced from a static context em Conta.depositar(100)?
Resumo
- Métodos de instância são declarados sem
statice pertencem a cada objeto. - Chamada:
objeto.metodo(argumentos). O objeto antes do ponto é o "objeto atual" durante a execução. - Dentro do método, atributos e outros métodos do mesmo objeto são usados direto pelo nome; os de outro objeto, com
outro.algo. - Coloque as regras (como "não sacar mais do que o saldo") dentro dos métodos da classe; o
mainsó pede. static= não depende de objeto (Math.sqrt); de instância = depende dos dados do objeto (texto.length()).- Chamar um método de instância pela classe ou de um contexto
staticsem objeto:non-static method … cannot be referenced from a static context.