Buscar

Aula 06 - LPG

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 15 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 15 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 15 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

Aula 06: POLIMORFISMO 
 
Objetivos 
 
 
 Recordando 
 
Na última aula vimos o conceito de herança que resumidamente poderíamos dizer 
que: 
A herança é um importante recurso da programação orientada a objeto que permite 
derivar uma nova classe mais especializada a partir de outra mais genérica existente. A 
classe derivada é chamada de subclasse, enquanto a classe original é dita superclasse. 
A subclasse assume (herda) os atributos (variáveis de instância) da superclasse e pode 
adicionar novos atributos exclusivamente seus. Quanto aos métodos, a subclasse pode 
utilizar (herdar) ou alterar os existentes na superclasse, bem como pode criar novos para 
seu uso. Os construtores não são herdados. 
 
 
 
 
 Membros da subclasse 
 
Ao escrever métodos na subclasse pode-se: 
 
1. sobrescrever (ou sobrepor) métodos da superclasse. Um método da subclasse 
sobrescreve um da superclasse se ele tem a mesma assinatura (mesmo nome e 
mesmos tipos de parâmetros) do método da superclasse. Deve ter, inclusive, o mesmo 
tipo de retorno. Sempre que aplicado a um objeto do tipo da subclasse, o método da 
subclasse será chamado. 
 
2. herdar métodos da superclasse. Todo método da superclasse que não é sobrescrito 
na subclasse é herdado por esta. O método da superclasse pode ser aplicado a um 
objeto da subclasse. 
 
3. definir novos métodos para a subclasse que não aparecem na superclasse. Esses 
métodos só poderão ser aplicados a objetos da subclasse. 
 
 
 
 
 
 
 Compreender tipos estáticos e dinâmicos 
 Definir o conceito de polimorfismo de método 
 Apresentar classes e métodos abstratos 
 Esclarecer o uso do operador this 
 Apresentar classes e métodos final 
 
 
 
Para os atributos, não se pode sobrescrever nenhum atributo da superclasse. Pode-se 
apenas: 
 
1. herdar os atributos da superclasse. Todos os atributos da superclasse são 
automaticamente herdados pela subclasse. Todo objeto da subclasse terá cada um 
desses atributos. 
 
2. definir novos atributos. Só objetos da subclasse terão esses atributos. 
 
 
 
 Chamando métodos da subclasse 
 
Quando um objeto da subclasse chama um método, temos as seguintes situações: 
 
1. Se for um método da subclasse que sobrescreve um método da superclasse, o da 
subclasse é acionado (ele esconde o da superclasse). 
 
2. Se o método chamado só existe na superclasse, ele será acionado (a subclasse 
herda métodos da superclasse que ela não sobrescreve). 
 
3. Se o método é exclusivo da subclasse, ele será acionado. 
 
4. Se o método chamado não existe na subclasse, nem na superclasse, nem na 
superclasse da superclasse, e em nenhuma superclasse hierarquia acima, então 
acontece um erro. 
 
 
 Construtores da subclasse 
 
Os construtores da superclasse não são herdados pela subclasse, logo, esta deverá ter 
seus próprios construtores para inicializar seus atributos. Todavia, ao instanciar um objeto 
da subclasse, devemos inicializar aqueles atributos que ela está herdando, e o fazemos 
chamando algum construtor da superclasse, usando a chamada super(...). 
Obs. Se o construtor da subclasse não chama um da superclasse, então o construtor 
padrão da superclasse é chamado automaticamente. Se a superclasse não tem um 
construtor padrão, dá erro de compilação. 
 
 
 
 
 
 Polimorfismo 
 
De um modo geral, polimorfismo é a realização de uma tarefa de formas diferentes (poli 
– muitas; morphos – formas). Uma utilização importante do polimorfismo em Java 
ocorre nas hierarquias de herança, quando dois objetos, sendo um da superclasse e 
outro da subclasse, respondem diferentemente à mesma chamada de um método. 
Antes de vermos um exemplo, vamos estudar a conversão de objetos numa hierarquia 
de herança. 
 
 Conversão de um tipo para outro mais genérico (Upcasting) 
 
Observe a seguinte hierarquia de herança. 
 
A instrução Pai p; declara p como uma referência da superclasse Pai – isto define a 
vocação de p: referenciar qualquer objeto do tipo Pai. 
Atente, agora, para a seguinte regras: 
Regra 1: 
Em Java, podemos atribuir um objeto da subclasse a uma referência de sua 
superclasse. (Esta operação é chamada upcasting, de up type casting). 
 
Esta regra é razoável, pois todo objeto da subclasse é um objeto da sua superclasse. 
Então, podemos atribuir a p um objeto Filha: 
Pai p; 
p = new Filha(); 
 
Agora p, uma referência de superclasse, está apontando um objeto de subclasse. A 
vocação de p não foi contrariada, pois o objeto Filha é também um objeto Pai. 
Observe, a seguir, p acionando métodos: 
p.m1(); //chama m1( ) de Filha, porque ela sobrescreveu o m1 de Pai 
 
p.m2(); //chama m2( ) de Pai, herdado por Filha 
 
Porém, cuidado com a chamada seguinte: 
p.m3(); //ERRO de compilação 
 
Este último comando foi uma tentativa de acessar, através de p (referência de 
superclasse) um membro exclusivo da subclasse, o que contraria a vocação de p, 
expressa na regra 2: 
 
 
Regra 2. 
Uma referência de superclasse só reconhece membros disponíveis na superclasse, 
mesmo que esteja apontando para um objeto de subclasse. 
 
A figura ao lado tenta mostrar, de outra forma, a relação 
entre Pai e Filha. A Filha herda do Pai todo o seu 
conhecimento (atributos) e comportamentos (métodos), 
mas pode acrescentar conhecimento e comportamentos 
novos exclusivamente seus, aos quais o Pai não tem 
acesso. A parte escura da figura mostra o que o Pai 
sabe. 
 
 Resumindo, o upcasting é a conversão de um objeto de tipo mais específico para 
um tipo mais genérico, feita implicitamente através de atribuição, mas a partir da 
conversão só os membros do tipo mais genérico podem ser acessados sem artifícios. 
Para o nosso exemplo, resta a questão: como acessar o método m3? A resposta está 
no downcasting. 
 
 Conversão de um tipo para outro mais específico (Downcasting) 
 
Regra 3 
Em Java, a atribuição de um objeto de superclasse a uma referência de subclasse, 
sem uma coerção explícita, não é permitida. 
Ex: 
Filha f = p; // ERRO de compilação 
 
Isto parece, também, razoável, pois f tem vocação de referenciar e saber coisas que 
não existem no objeto Pai (o atributo y e o método m3). Ou ainda, a relação É UM tem 
mão única, da subclasse para a superclasse, portanto, um objeto Pai não é um objeto 
Filha. Há casos, todavia, em que podemos "forçar a barra" através de coerção, se 
soubermos que o objeto atualmente com referência de superclasse é, na realidade, um 
objeto da subclasse para a qual estamos convertendo. Assim, poderíamos chamar o 
método m3. 
Filha f = (Filha) p; //nome da classe destino entre parênteses 
f.m3(); 
 
Esta realidade, citada na frase anterior, é algo verificado por Java apenas em tempo de 
execução. Se o objeto for do tipo da subclasse, a coerção será válida, mas se não for, 
ocorrerá uma ClassCastException. 
Para proteger nosso código dessa incerteza, convém usar o operador especial 
instanceof com a seguinte sintaxe: 
variável objeto instanceof Classe 
Este operador retorna true se a variável objeto é do tipo da Classe, false em caso 
contrário. Assim, nossa coerção anterior ficaria mais segura se codificada assim: 
if (p instanceof Filha){ 
 
Filha f = (Filha) p; 
f.m3(); 
} 
 
Observações: 
1. downcasting vem de down type casting. 
2. O downcast só será válido se, em tempo de execução, o objeto tiver um 
relacionamento É UM com o tipo dado entre parênteses – ou seja, (C)ref só 
vale se ref É UM C. 
 
Animal a; Macaco m; Girafa g; 
m = new Macaco(); 
a = m; 
g = (Girafa) m; //Erro em tempo de compilação 
g = (Girafa) a; //Erro em tempo de execução 
 
 
Uma aplicação prática do polimorfismo 
 
O exemplo seguinte, embora simples, mostra, na classe TestaAlteta,a presença do 
polimorfismo, ao permitir instanciar, através de uma mesma referência de superclasse, 
tanto objetos desta como de sua subclasse. 
 
Numa academia, os atletas são categorizados pelo peso, conforme tabela da esquerda. Os 
lutadores, porém, têm categorias definidas pela da direita. Mas, é obvio, todo lutador é um 
atleta. 
 
 
 
Temos, então, a seguinte hierarquia de herança : 
 
 
 
A codificação correspondente em Java: 
--------------------------------------------------------------- 
Classe Atleta 
--------------------------------------------------------------- 
public class Atleta{ 
 
private int peso; 
 
public Atleta(int p){ 
peso = p; 
} 
 
public String defineCategoria(){ 
String cat; 
if (peso <= 50) 
cat = "Infantil"; 
else if (peso <= 65) 
cat = "Juvenil"; 
 else 
cat = "Adulto"; 
return cat; 
} 
 
public int getPeso(){ 
return peso; 
} 
} 
--------------------------------------------------------------- 
Classe Lutador 
--------------------------------------------------------------- 
public class Lutador extends Atleta{ 
 
public Lutador(int p){ 
super(p); 
} 
 
public String defineCategoria(){ 
String cat; 
if (getPeso() <= 54) 
cat = "Pluma"; 
else if (getPeso() <= 60) 
cat = "Leve"; 
 else if (getPeso() <=75) 
cat = "Meio-leve"; 
else 
cat = "Pesado"; 
return cat; 
} 
 
} 
 
 
 
 
 
--------------------------------------------------------------- 
Classe Principal 
--------------------------------------------------------------- 
public class TestaAtleta{ 
 
public static void main(String args[]) { 
Teclado t = new Teclado(); 
int n = t.leInt("Quantidade de atletas: "); 
int r, p; 
Atleta a; //referência da superclasse 
for (int i = 0; i < n ; i++){ 
p = t.leInt("Peso: "); 
r = (int)(Math.random() * 10); // sorteio para instanciar um 
if (r % 2 == 0) // atleta ou um lutador 
a = new Atleta(p); 
else 
a = new Lutador(p); //aqui ocorre upcasting 
System.out.println("Categoria: " + a.defineCategoria()); 
} 
} 
} 
 
Veja que a referência de superclasse a é usada para instanciar ora um objeto da superclasse, 
ora um da subclasse. E a chamada do método defineCategoria, na última linha, aciona o 
código correspondente à forma que o objeto tomou dentro do if: ou a forma de um atleta, ou 
a forma de um lutador. O tipo real do objeto é que determina o método a ser chamado. 
Sugerimos que o aluno execute no computador o código acima e digite sempre o mesmo 
peso para diversas instanciações. 
 
Observações: 
a. Se a academia do exemplo vier a criar no futuro outra modalidade de atleta, por 
exemplo, nadador, com outras regras para a definição de sua categoria, bastará estender a 
classe Atleta, como foi feito para o lutador, sem afetar as classes já prontas. Chama-se esta 
capacidade de extensibilidade, que é uma das vantagens do polimorfismo. 
 
b. O polimorfismo como foi apresentado aqui é, para a maioria das pessoas, o 
verdadeiro polimorfismo. Só existe em hierarquias de herança ou interface. Todavia, a 
sobrecarga é também um tipo de polimorfismo. Alguns autores destacam os dois tipos, caso 
em que chama-se o que estudamos de polimorfismo tipo sobreposição (ligação tardia, 
dinâmica ou dynamic binding) e o outro de polimorfismo tipo sobrecarga (ligação precoce, 
estática ou early binding). A diferença principal é que na sobrecarga quem escolhe o método 
a ser acionado é o compilador e no polimorfismo com ligação dinâmica quem decide é a 
JVM. 
 
 
Não confundir sobrecarga com sobrescrita 
 
Um método sobrecarrega outro se ambos têm o mesmo nome, mas parâmetros de tipos ou quantidades 
diferentes. Os dois métodos podem estar na mesma classe ou um na superclasse e outro na subclasse. A 
sobrecarga se aplica também a construtores de uma mesma classe. 
 
Um método de uma subclasse sobrescreve um da sua superclasse se tem exatamente a mesma assinatura 
(nome, parâmetros e tipo de retorno) do método da superclasse (tem apenas uma outra implementação). 
Obviamente, não pode haver sobrescrita de métodos numa mesma classe ou sobrescrita de construtores. 
 
 Classes e métodos abstratos 
Na aula passada, criamos duas classes utilizando o conceito de herança e colocamos na 
superclasse o que era comum às duas classes. Entretanto, cabe observar que era previsto 
haver empregados (objetos, portanto) de ambas as classes (EmpComissionado e 
EmpFixoComissionado). 
Existem situações em que classes possuem um conjunto de características comuns que, 
entretanto, não é suficiente para que com estes seja possível definir objetos reais. Esse 
conjunto de características comuns é então separado em uma superclasse destinada 
apenas a definir o que é comum às subclasses dela derivadas, sem que se tenha a 
intenção de efetivamente instanciar objetos dessa classe. Tal classe é conhecida como 
Classe Abstrata. 
 
Verificando este assunto a partir de um exemplo simples. 
Ex. Uma empresa contrata empregados através de uma das duas modalidades de 
pagamento: mensalistas ou horistas. O empregado mensalista recebe um salário básico 
mensal fixo mais as horas extras que ele faz no mês. O valor de uma hora-extra 
corresponde a 1/160 do salário básico mensal. O empregado horista simplesmente recebe 
pelas horas trabalhadas no mês, não existindo o recurso de hora extra. 
Percebe-se, facilmente, a presença da seguinte hierarquia: 
 
Todavia, nenhum empregado jamais será instanciado na superclasse, uma vez que ele 
deve ser obrigatoriamente enquadrado numa das duas categorias: mensalista ou horista. 
A classe empregado, neste caso, é dita uma classe abstrata. 
Uma classe abstrata é aquela que não pode ser instanciada. É uma classe que existe 
para ser herdada obrigatoriamente. Constitui-se num "molde" a partir do qual subclasses 
serão construídas. 
Em Java, indica-se com o modificador abstract antes da palavra class. 
public abstract class Empregado{ 
private String nome; 
 .......... 
Uma classe que realmente instancia objetos é dita uma classe concreta. É o caso das 
nossas classes Horista e Mensalista. 
Uma superclasse abstrata é extremamente genérica, especificando apenas características 
comuns a qualquer subclasse possível. Em nosso exemplo, todo empregado tem um 
nome. 
Uma classe abstrata possui, em geral, um ou mais métodos abstratos. 
Um método abstrato é um método composto apenas de assinatura e sem 
implementação (sem bloco de código). A implementação deverá ser feita 
obrigatoriamente pelas subclasses concretas. Indica-se método pelo modificador 
abstract antes do tipo de retorno. 
public abstract double calculaLiquidoMes(int horas); 
 
Este método abstrato na classe Empregado obriga sua sobrescrita em subclasses 
concretas. Assim, cada categoria específica de empregado irá implementar de forma 
diferente o cálculo do valor líquido que o empregado irá receber ao final do mês. 
 
Observações: 
• Construtores não podem ser abstratos 
• Uma classe que contém um ou mais métodos abstratos deve ser declarada como 
abstrata, mesmo que tenha métodos não abstratos 
• Cada subclasse concreta de uma superclasse abstrata deve implementar os métodos 
abstratos da superclasse 
• Se uma classe estende outra que tem método abstrato e não o implementa, então ela 
também é abstrata e deve ser declarada como tal 
 
Para o projeto da aula anterior 
poderíamos ter criado a estrutura ao 
lado, onde Empregado fosse uma classe 
reservada para conter as características 
comuns a todos os empregados. Essa 
classe, entretanto, não possuiria todos 
os dados necessários para definir um 
empregado real, o que implica em quenão haverá nenhum objeto real dessa 
classe, caracterizando-a como uma 
classe abstrata. 
Essa classe deve conter apenas atributos que sejam comuns a todos os empregados, 
como nome, sobrenome e CPF. Nas classes derivadas é que aparecerão atributos 
relacionados à forma de contratação e que influenciam a remuneração dos empregados, 
tais como total de vendas, bônus, horas trabalhadas, salário base, etc. 
Nesse contexto, para que possamos utilizar corretamente o polimorfismo de método no 
cálculo do salário, devemos definir um método abstrato salário() na superclasse 
Empregado. Tal método não deve possuir efetivamente nenhuma implementação, pois 
não é sequer possível calcular o salário nessa classe. O código efetivo de implementação 
do método salário() deve ser deixado a cargo das subclasses, que possuem as 
informações necessárias para realizar o cálculo do salário. Esse método da superclasse 
Empregado é chamado de Método Abstrato e sua sobrescrição nas subclasses permite o 
Polimorfismo de Método. 
Na aula passada, quando implementamos diferentes versões para o método construtor de 
uma classe, fizemos uma Sobrecarga de Método. De fato podemos fazer isto com 
qualquer método da classe. Como visto no caso do construtor, o que diferencia essas 
implementações e permite à linguagem determinar qual implementação efetivamente 
chamar é a quantidade de argumentos definidos em cada uma e a quantidade de 
parâmetros passados na chamada do referido método. 
No exemplo de hoje, que será uma adaptação do exemplo da aula passada, faremos uma 
sobrecarga do método salario na classe EmpAssalariado, de forma a permitir dois 
cálculos diferentes para o salário, um com um abono (passado como parâmetro) e outro 
sem abono. 
 
 Operador this 
Novamente recordando a aula passada, quando criamos os métodos parametrizados, 
declaramos os argumentos com nomes “parecidos” com os atributos da classe. Fazendo 
dessa forma, ficou claro que o argumento é uma variável e o atributo é outra, como, por 
exemplo, em: 
public EmpComissionado (String n, String sn, String cic, double vendas,double comissao){ 
 nome = n; 
 sobrenome = sn; 
 cpf = cic; 
 setTotalVendas (vendas); 
 setTaxaComissao (comissao); 
} 
No construtor acima estamos lidando com dez variáveis (cinco argumentos do método e 
cinco atributos da classe). Esta forma, entretanto, não favorece a compreensão, na 
medida que alguns nomes de argumentos são bastante esclarecedores do seu conteúdo e 
criar um sinônimo pode ser indesejável. Porem, razões sintáticas nos fizeram utilizar os 
sinônimos, pois se o argumento sn, por exemplo, fosse chamado sobrenome, teríamos o 
comando de atribuição: 
 sobrenome = sobrenome; 
Isto não seria corretamente compreendido pelo compilador. Neste caso estamos diante de 
uma sobrecarga de nome e o compilador resolve isso escolhendo a referência mais 
próxima (a que foi definida por último). Portanto, a variável sobrenome referenciada 
nesse comando não seria o atributo da classe e sim o argumento do método (o último 
definido). 
Soluciona-se isso usando o operador this, que faz referência ao objeto atual (objeto 
corrente ao tempo da execução). Assim o trecho de programa anterior poderia ser mais 
elegantemente definido assim: 
public EmpComissionado (String nome, String sobrenome, 
 String cpf, double totalVendas,double taxaComissao){ 
 this.nome = nome; 
 this.sobrenome = sobrenome; 
 this.cpf = cpf; 
 setTotalVendas (totalVendas); 
 setTaxaComissao (taxaComissao); 
} 
O mesmo se aplica aos demais métodos, como, por exemplo, aos métodos set dos 
atributos das classes. 
 Classes e métodos final 
Podem ocorrer situações em que não seja desejável permitir que eventuais subclasses 
sobrescrevam métodos de uma superclasse. Nestes casos devemos impedir a 
redefinição do método através do uso de uma palavra reservada na declaração do 
mesmo. As declarações de métodos precedidas pela palavra reservada final não podem 
ser sobrescritas em subclasses. Os métodos declarados como private são 
implicitamente final porque é impossível sobrescrevê-los em uma subclasse. Como as 
implementações dos métodos final não podem mudar, todas as subclasses utilizam a 
mesma implementação do método, e as chamadas aos métodos final são resolvidas em 
tempo de compilação, o que é conhecido como vinculação estática. 
 
 
Uma classe declarada como final não pode ser superclasse, ou seja, nenhuma classe 
pode herdar de uma classe declarada como final. 
Tornar uma classe final pode ser necessário, por exemplo, para atender a requisitos de 
segurança, impedindo que programadores criem subclasses que poderiam “driblar” 
restrições de segurança. 
Já atributos declarados como final se comportam como constantes e devem ser 
iniciados na declaração ou no construtor da classe, não podendo ser reescritos 
posteriormente. 
Exemplos: 
 private final int fator = 2; // atributo final 
 protected final void abrir(); // metodo final 
 public final class conta { // classe final 
 ... 
 } 
 Revendo a aplicação 
Para consolidar os conceitos de classe abstrata, método abstrato, sobrecarga de método, 
polimorfismo de método, operador this e operador final, vamos alterar a aplicação que 
fizemos na aula passada para o código que se segue. 
Primeiramente, vamos adicionar a classe abstrata Empregado, que possui apenas três 
atributos (nome, sobrenome e cpf). Essa classe impede que classes derivadas alterem os 
métodos set desses atributos (uso do final) e usa sobrecarga de nomes para receber 
argumentos no construtor com os mesmos nomes dos atributos (uso do this): 
//Classe Empregado 
public abstract class Empregado extends Object { 
 protected String nome; 
 protected String sobrenome; 
 protected String cpf; 
 public Empregado (// Construtor com 3 argumentos 
 String nome, String sobrenome, String cpf){ 
 this.nome = nome; // uso do operador this 
 this.sobrenome = sobrenome; 
 this.cpf = cpf; 
 } 
 public final void setNome (String nome){//usa final 
 this.nome = nome; 
 } 
 public String getNome() { 
 return nome; 
 } 
 public final void setSobrenome (String sobrenome){ 
 this.sobrenome = sobrenome; 
 } 
 public String getSobrenome() { 
 return sobrenome; 
 } 
 public final void setCpf (String cpf){ 
 this.cpf = cpf; 
 } 
 public String getCpf() { 
 return cpf; 
 } 
 public abstract double salario(); // Método abstrato 
} // fim da classe Empregado 
 
A seguir vamos reescrever a classe EmpComissionado para herdar da classe abstrata 
Empregado, fazendo as adaptações necessárias no construtor. 
 
//Classe Empregado Comissionado 
public class EmpComissionado extends Empregado { 
 protected double totalVendas; 
 protected double taxaComissao; 
 protected double bonus; 
 public EmpComissionado (String nome, 
 String sobrenome, String cpf, double totalVendas, 
 double taxaComissao, double bonus){ 
 // chama construtor da classe abstrata 
 super (nome, sobrenome, cpf); 
 setTotalVendas (totalVendas); 
 setTaxaComissao (taxaComissao); 
 setBonus(bonus); 
 } 
 public void setTotalVendas (double totalVendas){ 
 this.totalVendas = 
 (totalVendas < 0.0) ? 0.0 : totalVendas; 
 } 
 public double getTotalVendas () { 
 return totalVendas; 
 } 
 public void setTaxaComissao (double taxaComissao){ 
 this.taxaComissao = 
 (taxaComissao < 0.0) ? 0.0 : taxaComissao; 
 } 
 public double getTaxaComissao () { 
 return taxaComissao; 
 } 
 publicvoid setBonus (double bonus){ 
 this.bonus = (bonus < 0.0) ? 0.0 : bonus; 
 } 
 public double getBonus (){ 
 return bonus; 
 } 
 public double salario(){ // Calcula o salário 
 return (taxaComissao * totalVendas + 
 getBonus() * 2 * taxaComissao * totalVendas); 
 } 
 public String toString () { 
 return String.format ( 
 "%s%s %s\n%s%s\n%s%.2f\n%s%.2f\n%s%.2f", 
 "Identificacao: ", nome, sobrenome, 
 "CPF: ", cpf, 
 "Total de vendas: ", totalVendas, 
 "Taxa de comissão: ", taxaComissao, 
 "Salario total: ", salario() ); 
 } 
} // fim da classe Empregado Comissionado 
 
A classe EmpFixoComissionado continua herdando da classe EmpComissionado. Só 
sofreu alteração nos métodos toString() e salario() que agora contemplam o proposto no 
desafio da aula passada: 
 
//Classe Empregado com salário base mais Comissão 
public class EmpFixoComissionado extends EmpComissionado { 
 private double salarioBase; // atributo exclusivo 
 // Construtor com 7 argumentos 
 public EmpFixoComissionado ( String nome, 
 String sobrenome, String cpf, double totalVendas, 
 double taxaComissao, double salarioBase, 
 double bonus){ 
 // chama construtor da superclasse EmpComissionado 
 super (nome, sobrenome, cpf, totalVendas, 
 taxaComissao,bonus); 
 // inicia atributo próprio da classe 
 setSalarioBase (salarioBase); 
 } 
 public void setSalarioBase (double salarioBase){ 
 this.salarioBase = 
 (salarioBase < 0.0) ? 0.0 : salarioBase; 
 } 
 public double getSalarioBase() { 
 return salarioBase; 
 } 
 public double salario(){ // Calcula o salário 
 return (salarioBase + 
 taxaComissao * totalVendas + 
 getBonus() * taxaComissao * totalVendas); 
 } 
 public String toString () { 
 return String.format ("%s%s %s\n%s%s\n%s%.2f 
 \n%s%.2f\n%s%.2f\n%s%.2f", 
 "Identificacao: ", nome, sobrenome, 
 "CPF: ", cpf, 
 "Total de vendas: ", totalVendas, 
 "Taxa de comissão: ", taxaComissao, 
 "Salario base: ", salarioBase, 
 "Salario total: ", salario() ); 
 } 
} // fim da classe Empregado Fixo Comissionado 
 
 
 
A nova classe EmpAssalariado herda diretamente da classe abstrata: 
//Classe Empregado Assalariado 
public class EmpAssalariado extends Empregado { 
 private double salarioMensal; // atributo exclusivo 
 // Construtor com 4 argumentos 
 public EmpAssalariado (String nome, 
 String sobrenome, String cpf, 
 double salarioMensal){ 
 // chama construtor da classe abstrata 
 super (nome, sobrenome, cpf); 
 setSalarioMensal(salarioMensal); 
 } 
 public void setSalarioMensal (double salarioMensal){ 
 this.salarioMensal = salarioMensal; 
 } 
 public double getSalarioMensal(){ 
 return salarioMensal; 
 } 
 public double salario(){ // Salário sem abono 
 return (getSalarioMensal()); 
 } 
 public double salario(double abono){ //sal. c/ abono 
 return (getSalarioMensal() + abono); 
 } 
 public String toString () { 
 return String.format ( 
 "%s%s %s\n%s%s\n%s%.2f\n%s%.2f\n", 
 "Identificacao: ", nome, sobrenome, 
 "CPF: ", cpf, "Salario total: ", salario(), 
 "Salario total com abono: ", salario(100.0)); 
 } 
} // fim da classe Empregado Assalariado 
Finalmente, o código de teste (classe Principal) fica como se segue (agora só está 
imprimindo chamando o método toString das classes): 
public class Principal{ 
 public static void main ( String argv[]) { 
 EmpFixoComissionado empregadoFC = 
 new EmpFixoComissionado ("Roberto", "Silveira", 
 "123456789-10", 10000, 0.02, 300, 0.01); 
 EmpComissionado empregadoC = 
 new EmpComissionado ("Francisco", "Oliveira", 
 "987654321-00", 20000, 0.02, 0.01); 
 EmpAssalariado empregadoA = 
 new EmpAssalariado ("Joaquim", "Alberto", 
 "192837465-99", 1000); 
 System.out.printf ("\n%s: \n\n%s\n\n", 
 "Informacoes do empregado comissionado:", 
 empregadoC.toString() ); 
 System.out.printf ("\n%s: \n\n%s\n\n", 
 "Informacoes do empregado fixo comissionado:", 
 empregadoFC.toString() ); 
 System.out.printf ("\n%s: \n\n%s\n\n", 
 "Informacoes do empregado assalariado:", 
 empregadoA.toString() ); 
 } // fim do método main 
} // fim da classe Principal 
Compile e execute o código acima. 
 
 Desafios 
1. Altere o código da classe de teste (Principal) para que o empregado assalariado (objeto 
empregadoA) seja declarado como da classe estática Empregado mas continue sendo 
instanciado como da classe EmpAssalariado. Execute e veja que funciona perfeitamente 
o polimorfismo de métodos. 
2. Agora tente alterar também a classe de instanciação do objeto empregadoA para 
Empregado. Compile e explique a ocorrência de erro. 
3. Atendendo ao diagrama de classes da página dois desta aula, implemente também a 
classe EmpHorista. Crie os métodos e atributos que julgar necessários para que os 
objetos desta classe tenham um comportamento semelhante aos objetos das demais 
classes do projeto. Altere a classe Principal para que esta instancie e teste também um 
objeto empregadoHorista, imprimindo suas informações, inclusive seu salário, que deve 
ser calculado com base em um valor constante (use um atributo final) representativo do 
valor da hora trabalhada, multiplicado pelo número de horas trabalhadas (um atributo 
exclusivo desta classe). 
4. Na classe de teste crie um empregado de cada classe declarada, inicie o objeto com os 
valores adequados de parâmetros e imprima um relatório com as informações de cada 
empregado a partir de chamadas aos métodos toString de cada classe. 
5. Altere o exemplo criado no exercício 4 da aula 3, que envolvia a criação de uma super 
classe FiguraGeometrica e de classes filhas Quadrado e Circulo. A alteração deve ser no 
sentido de que a superclasse deve passar a ser abstrata e os métodos para cálculo de área 
e perímetro devem passar a ser abstratos também (ao invés de implementar na 
superclasse um método inútil que retorna sempre zero, como foi feito na aula passada). 
Essa alteração força naturalmente a sobrescrição dos métodos de cálculo nas classes 
derivadas, de forma a realizarem as contas corretamente, de acordo com o tipo de figura 
da classe derivada. Após testar, introduza mais uma alteração, que consiste em criar mais 
duas classes, Quadrilatero (que deve ser subclasse de FiguraGeometrica e superclasse 
de Quadrado) e Retangulo (que também deve ser subclasse de Quadrilatero). A classe 
Quadrilatero deve possuir um atributo constante numLados que deve ser iniciado com o 
valor 4. Altere adequadamente os atributos e os métodos de cálculo das classes. Instancie 
um objeto de cada figura na classe de teste, criando-os com as referências estáticas e 
dinâmicas iguais e imprimindo todos os atributos e cálculos dos objetos. Por fim, altere a 
criação dos objetos na classe de teste, de forma que a referência estática passe a ser 
FiguraGeometrica. Isto é parecido com o que foi feito no desafio 1 desta aula, 
entretanto, observe que é gerado um erro de compilação ao ser chamado qualquer 
método get referente a um atributo específico das subclasses (tal como getRaio()), por 
exemplo. Explique esse erro.

Continue navegando