• Nenhum resultado encontrado

Orientação à Objetos – Interfaces

SOBRECARGA

INTERFACE

IMPLEMENTS

}

Agora sim! Podemos passar um ou outro. Mas aí dentro o código é igual! Devo imprimir o nome do usuário do sistema e ver sua senha.

O problema não para por aí. O dia que tivermos algum outro funcionário com permissão de acesso ao sistema, vamos ter de alterar a classe Relatorio, perdendo assim uma grande vantagem que tínhamos visto antes, o polimorfismo.

Métodos com mesmo nome

Em Java, métodos podem ter o mesmo nome desde que não sejam ambíguos, isso é, que exista uma maneira de distinguir no momento da chamada.

Isso se chama sobrecarga de método. (overloading, não confundir com overriding, que é um conceito muito mais importante).

O que poderíamos fazer nesta situação? O melhor seria poder tratar Gerente e Diretor de uma única forma. Poderíamos então mudar nossa árvore de herança?

Isolando os FuncionarioComAcesso do FuncionarioComum é muito interessante! Dessa forma, o relatório poderia receber apenas FuncionarioComAcesso.

Mas o problema persiste, só que agora de outra maneira: não podemos mais tratar FuncionarioComAcesso e Funcionario de uma forma igual, já que mudamos nossa modelagem. Então quando precisarmos de um relatório de funcionários, para saber quanto de salário deve ser pago, essa classe vai ter o mesmo problema que o relatório anterior.

9.2 - Interfaces

O que precisamos para resolver nosso problema? Arranjar uma forma de poder referenciar Diretor e Gerente.

Se existisse uma forma que essas classes pudessem garantir que possuem um determinado método, através de um contrato, resolveríamos o problema.

O contrato de um FuncionarioComAcesso seria algo como: “A classe Gerente se compromete a ser tratada como FuncionarioComAcesso, com isso, sendo obrigada a ter os métodos necessários”.

Podemos criar esse contrato em Java!

interface FuncionarioComAcesso { public String getSenha();

public void setSenha(Sring senha); }

Chama-se interface pois é a maneira a qual poderemos conversar com um FuncionarioComAcesso.

E o Gerente pode “assinar” o contrato, ou seja, implementar a interface. No momento que ele implementa essa interface, ele precisa escrever os métodos pedidos pela interface. Para implementar usamos a palavra chave implements na classe:

class Gerente extends Funcionario implements FuncionarioComAcesso { private String senha;

public String getSenha() { return senha;

}

public void setSenha(Sring senha) { this.senha = senha;

} }

A partir de agora, podemos tratar um Gerente como sendo um FuncionarioComAcesso. Quando crio uma variável do tipo FuncionarioComAcesso, estou criando uma referência para qualquer objeto de uma classe que implementa FuncionarioComAcesso:

FuncionarioComAcesso func = new Gerente(); System.out.println(func.getSenha());

Novamente, o proveito desse polimorfismo é receber como argumento. Vamos voltar ao nosso relatório de senhas:

class RelatorioSenhas {

void adiciona(FuncionarioComAcesso func) { System.out.println(func.getNome());

System.out.println(“Senha: ” + func.getSenha()); }

}

Pronto! E já podemos passar qualquer FuncionarioComAcesso para o RelatorioSenhas. Então precisamos fazer com que o Diretor também implemente essa interface.

class Diretor extends Funcionario implements FuncionarioComAcesso { private String senha;

public String getSenha() { return senha;

}

public void setSenha(Sring senha) { if (senha.length() < 7)

return; // um Diretor só pode ter senhas de,

// no mínimo, 7 caracteres

this.senha = senha; }

}

Você pode implementar mais de uma interface!

Diferentemente das classes, uma interface pode herdar de mais de uma interface. É como um contrato que depende de que outros contratos sejam fechados antes deste valer. Você não herda métodos e atributos, e sim responsabilidades.

Agora podemos passar um Diretor. No dia em que tivermos mais um funcionário com acesso ao sistema, basta que ele implemente essa interface, para se encaixar no sistema.

9.3 - Um pouco mais...

1-) Posso substituir toda minha herança por interfaces? Qual é a vantagem e a desvantagem?

2-) Uma interface também pode declarar constantes (não atributos de objeto). Qual é a utilidade?

9.4 - Exercícios

1-) interface AreaCalculavel{ double calculaArea(); }

class TapedeQuadrado implements AreaCalculavel {

// ...

}

class TapedeCircular implements AreaCalculavel {

// ...

}

class TapeteRetangular implements AreaCalculavel {

// ...

}

2-) Tributável:

interface Tributavel {

double calculaTributos(double taxa); }

class ContaCorrente extends Conta implements Tributavel { }

class SeguroDeVida implements Tributavel { }

Crie um GerenciadorDeImpostoDeRenda que recebe todos os tributáveis de uma pessoa e soma seus valores:

class GerenciadorDeImpostoDeRenda { private double taxa;

private double total;

GerenciadoDeImpostoDeRenda(double taxa) { this.taxa = taxa;

}

void adiciona(Tributavel t) {

System.out.printl(“Adicionando tributavel: ” + t);

// somar aqui o valor dos tributos ao total e imprimir o total atual

} }

Crie um main para instaciar diversas classes que implementam Tributavel e passar como argumento para um GerenciadorDeImpostoDeRenda. Repare que você não pode passar qualquer tipo de conta para o método adiciona, apenas a que Capítulo 9 - Orientação à Objetos – Interfaces - Página 67

implementa Tributavel. Além disso pode passar o SeguroDeVida.

Se você precisar agora de uma SeguroDeVida, e essa classe também é Tributável, você precisa fazer o que?

(Opcional) Transforme a classe Conta em uma interface. Atenção: faça isso num projeto a parte pois usaremos a Conta como classes nos exercícios futuros.

interface Conta {

double getSaldo();

void deposita(double valor); void retira(double valor); void atualiza(double taxaSelic); }

Adapte ContaCorrente e ContaPoupanca para essa modificacao:

class ContaCorrente implements Conta {

// ...

}

class ContaPoupanca implements Conta {

// ...

}

Algum código vai ter de ser copiado e colado? Isso é tão ruim? Como você poderia diminuir esse copia e cola e centralizar esses códigos repetidos em um lugar só?

(Opcional) Subinterfaces: As vezes

interface ContaTributavel extends Conta, Tributavel { } class ContaCorrente implements ContaTributavel {

// metodos

}

Conta c = new ContaCorrente(); Tributavel t = new ContaCorrente();

Repare que o código pode parecer estranho pois a interface não declara método algum, só herda os métodos abstratos declarados nas outras interfaces. Repare também que uma interface pode estender de mais de uma interface, sendo que classe só pode estender de uma (herança simples).

10

Exceções – Controlando os erros

“Quem pensa pouco, erra muito” Leonardo da Vinci -

Ao término desse capítulo, você será capaz de:

• controlar erros e tomar decisões baseadas nos mesmos;

• criar novos tipos de erros para sua melhorar o tratamento dos mesmos em sua aplicação ou biblioteca;

• assegurar que um método funcionou como diz em seu "contrato".

10.1 - Exceção

Voltando às Contas que criamos no capítulo 6, o que iria acontecer ao tentar chamar o método saca com um valor fora do limite? O sistema iria mostrar uma mensagem de erro, mas quem chamou o método saca não irá saber que isso aconteceu.

Como avisar aquele que chamou o método que ele não conseguiu fazer aquilo que deveria?

Em Java, os métodos dizem qual o contrato que eles devem seguir, se ao tentar sacar ele não consegue fazer aquilo que deveria, ele precisa ao menos avisar o usuário que tentou sacar que isso não foi feito.

Veja no exemplo abaixo: a velocidade máxima é 100 e tentamos acelerar 1000. Nesse instante não sabemos se a chamada de acelerar funcionou ou não. Conta minhaConta = new Conta();

minhaConta.setSaldo(100); minhaConta.setLimite(100); minhaConta.saca(1000);

// o saldo é -900? É 100? É 0? A chamada ao método saca funcionou?

Em sistemas de verdade, é muito comum que quem saiba tratar o erro é aquele que chamou o método e não a própria classe! Portanto, nada mais natural que a classe sinalizar que um erro ocorreu.

A solução mais simples utilizada antigamente é a de marcar o retorno de um método como boolean e retornar true se tudo ocorreu da maneira planejada ou false caso contrário:

boolean saca(double quantidade) {

if (quantidade > this.saldo + this.limite) { //posso sacar até saldo+limite System.out.println(“Não posso sacar fora do limite!”);

return false; } else {

this.saldo = this.saldo – quantidade; return true;

} }