Baixe o app para aproveitar ainda mais
Prévia do material em texto
PONTIFÍCIA UNIVERSIDADE CATÓLICA DE MINAS GERAIS Unidade de Contagem Disciplina Curso Turno Período Linguagens de Programação Sistemas de Informação Noturno 3◦ Professor Camillo J. S. Oliveira Polimorfismo (parte 2) Na prática anterior, viu-se como fazer para ocultar métodos herdados e produzir um código polimórfico. Agora será ilustrado como o polimorfismo pode ser explorado para produzir um código mais robusto, mais genérico e obter uma maior reutilização de código. Observe o código a seguir: import java . app le t . Applet ; import java . awt . ∗ ; class Quadrado { private int x , y , lado ; private Color cor ; public Quadrado ( int px , int py , int l ) { x = px ; y = py ; lado = l ; } public void setCor ( Color c ) { cor = c ; } public void desenha ( Graphics g ) { Color velhaCor = g . getColor ( ) ; g . s e tCo lo r ( cor ) ; g . drawRect (x , y , lado , lado ) ; g . s e tCo lo r ( velhaCor ) ; } } class Circu lo { private int x , y , lado ; private Color cor ; public Circu lo ( int px , int py , int l ) { x = px ; y = py ; lado = l ; cor = Color . b lack ; } public void setCor ( Color c ) { cor = c ; } public void desenha ( Graphics g ) { Color velhaCor = g . getColor ( ) ; g . s e tCo lo r ( cor ) ; g . drawOval (x , y , lado , lado ) ; g . s e tCo lo r ( velhaCor ) ; } } import java . app le t . Applet ; import java . awt . Graphics ; import java . awt . Color ; class ListaDeFiguras { private Quadrado vetQ [ ] ; private Circu lo vetC [ ] ; private int tmax , cq , cc ; public ListaDeFiguras ( int t ) { vetQ = new Quadrado [ t ] ; vetC = new Circu lo [ t ] ; tmax = t ; cq = 0 ; cc = 0 ; } public void i n s e r e (Quadrado q ) { i f ( cq == tmax) return ; vetQ [ cq ] = q ; cq++; } public void i n s e r e ( C i r cu lo c ) { i f ( cc == tmax) return ; vetC [ cc ] = c ; cc++; } public void desenha ( Graphics g ){ for ( int i = 0 ; i < cq ; i++) vetQ [ i ] . desenha ( g ) ; for ( int i = 0 ; i < cc ; i++) vetC [ i ] . desenha ( g ) ; } } import java . app le t . Applet ; import java . awt . Graphics ; import java . awt . Color ; public class Pol imorf i smo extends Applet { ListaDeFiguras l f ; public void i n i t ( ) { l f = new ListaDeFiguras ( 1 0 ) ; l f . i n s e r e (new Quadrado ( 0 , 0 , 3 0 ) ) ; l f . i n s e r e (new Quadrado (100 , 100 , 80 ) ) ; l f . i n s e r e (new Circu lo ( 2 0 , 4 0 , 3 4 ) ) ; } public void paint ( Graphics g ) { l f . desenha ( g ) ; } } Para executar o código acima utilize o eclipse e pressione o mouse em Run as applet... ou crie um arquivo HTML com o seguinte código: <BODY> <APPLET CODE=Polimorfismo.class WIDTH=200 HEIGHT=200></APPLET> </BODY> o código acima é formado de basicamente quatro classes. As classes Quadrado e Circulo são praticamente idênticas e representam duas figuras geométricas. A Classe ListaDeFiguras representa um repositório de figuras. Nesta classe são gerenciados os objetos que são adicionados ao programa. Ela também possui um método desenha que desenha todos os objetos geográficos construídos. Finalmente, a classe Polimorfismo representa a nossa aplicação gráfica. Ela é construída na forma de um Applet, o que permite que o código seja executado dentro de um navegador Web. Antes de seguir para a próxima seção, pense nas seguintes questões: 1. É possível se utilizar herança no código acima? 2. Quais as vantagens poderiam ser obtidas com herança? Herança e polimorfismo atuando juntos para reuso de código class Figura { protected int x , y , lado ; protected Color cor ; public Figura ( int px , int py , int l ) { x = px ; y = py ; lado = l ; cor = Color . b lack ; } public void setCor ( Color c ) { cor = c ; } public void desenha ( Graphics g ) { } } class Quadrado extends Figura { public Quadrado ( int px , int py , int l ) { super (px , py , l ) ; } public void desenha ( Graphics g ) { Color velhaCor = g . getColor ( ) ; g . s e tCo lo r ( cor ) ; g . drawRect (x , y , lado , lado ) ; g . s e tCo lo r ( velhaCor ) ; } } class Circu lo extends Figura { public Circu lo ( int px , int py , int l ) { super (px , py , l ) ; } public void desenha ( Graphics g ) { Color velhaCor = g . getColor ( ) ; g . s e tCo lo r ( cor ) ; g . drawOval (x , y , lado , lado ) ; g . s e tCo lo r ( velhaCor ) ; } } No exemplo de código acima criou-se a classe Figura e as classes Quadrado e Circulo passam a ser sub-classes de Figura. Esta herança permitiu reutilização de código, onde apenas a parte diferente das classes teve que ser definida nas classes filha. Lembre-se que em uma aplicação mais completa, os métodos mover, posicionar, dimensionar, entre outros, todos poderiam estar na classe Figura, garantindo uma grande reutilização de código. Ou seja, a herança tornou possível a eliminação de rotinas redundantes entre as classes Quadrado e Circulo. Entretanto, apenas a herança ainda não produz nenhum efeito prático sobre a classe ListaDeFiguras. Veja a seguir a utilização de polimorfismo na classe ListaDeFiguras e tente compreender como o polimorfismo permitiu a construção de um código mais genérico baseado na classe Figura. class ListaDeFiguras { private Figura vet [ ] ; private int tmax ; private int c ; public ListaDeFiguras ( int t ) { tmax = t ; c = 0 ; vet = new Figura [ t ] ; } public void i n s e r e ( Figura f ) { i f ( c == tmax) return ; vet [ c ] = f ; c++; } public void desenha ( Graphics g ) { for ( int i = 0 ; i < c ; i++) vet [ i ] . desenha ( g ) ; } } No código fonte da classe acima, o vetor vet[] passou a ser um vetor polimórfico sobre a classe Figura. O laço da função desenha da classe ListaDeFiguras irá desenhar dinamicamente o objeto que estiver na memória no momento. Ou seja, se o vetor apontar para um quadrado, será executada a função desenha de um quadrado. Se for um círculo, será desenhado um círculo. Quando dispõe-se da referência para a classe “pai” só se pode acessar os métodos previstos na interface da classe “pai”. Por isso foi necessário criar o método desenha vazio na classe Figura. Porém, durante o polimorfismo, o Java automaticamente ativa a implementação correspondente no objeto apontado. Caso o programador precise saber exatamente que objeto está instanciado na memória e está sendo apontado por uma referência polimórfica, ele pode usar o comando instanceof. O comando instanceof retorna o nome da classe do objeto (mais baixa na hierarquia de herança). Por exemplo: i f ( vet [ i ] instanceof Circu lo ) System . out . p r i n t l n ( " Ci rcu lo " ) ; Classes abstratas Classes abstratas permitem que se definam métodos sem implementação, que devem ser redefinidos em classes derivadas. O que define se uma classe é abstrata ou não é a ocorrência de pelo menos um método abstrato. Classes abstratas não podem ser instanciadas, ou seja, nenhum objeto desta classe pode ser construído com a cláusula new. As classes derivadas de classes abstratas herdam todos os métodos, incluindo os abstratos. Um classe abstrata não precisa possuir métodos abstratos. Mas toda classe com métodos abstratos deve ser declarada como uma classe abstrata. As classes derivadas de classes abstratas são abstratas até que implementem os métodos abstratos. Observe a classe figura. Ela poderia ser uma classe abstrata. abstract class Figura { protected int x , y , lado ; protected Color cor ; public Figura ( int px , int py , int l ) { x = px ; y = py ; lado = l ; cor = Color . b lack ; } public void setCor ( Color c ) { cor = c ; } public abstract void desenha ( Graphics g ) ; } Interfaces Interfaces em Java são como classes onde tudo é abstrato. Interfaces não são estendidas, mas imple- mentadas pelas classes usando a palavra-chave implements. Define-se a interface de uma classe de objetos, deixando a implementação a cargo de cada especialização desta classe. Interfaces são usadas para definir um protocolo de comportamento que pode ser implementado por qualquer classe na hierarquia de classes. É permitido se criar hierarquia de interfaces. Definir uma interface é similara definir uma nova classe. Na declaração da interface, declara-se os atributos tais como nome da interface e se ela herda de outra interface. O corpo da interface contém as declarações de constantes e dos métodos da interface. Para que uma classe possa usar uma interface usa-se a palavra-chave implements. Ao contrário de classes abstratas, uma classe que implementa uma interface provê o código para a implementação de todos os métodos declarados na interface. Interfaces podem ser declaradas, mas não podem ser instanciadas, assim como classes abstratas. Exemplo: Alarmes e Dorminhocos A classe AlarmClock é um serviço que notifica objetos dorminhocos que um certo tempo passou. Um objeto dorminhoco deve fazer duas coisas: pedir para o Alarme acordá-lo após certo tempo e implementar o método wakeUp. Se o AlarmClock tem espaço na lista, ele registra o dorminhoco e começa uma nova tarefa (thread) para este dorminhoco. Um objeto que quiser usar o AlarmClock deve implementar o método. A interface Sleeper define o método wakeUp() mas não a implementa. Ela também define constantes úteis. As classes que implementarem esta interface “herdam” a constante e devem implementar o método wakeUp(). Todo objeto será também um dorminhoco. Para exemplificar o uso de interfaces veja o código de um relógio que deve ser acordado a cada segundo para atualizar o display do tempo. Pegue o código que está disponível no arquivo Alarme.zip e compile. Porque utilizar interfaces ao invés de classes abstratas? Uma interface é simplesmente uma coleção de métodos abstratos. Seria a classe abstrata Sleeper equiv- alente à interface? Resposta: Não. Se Sleeper fosse classe abstrata apenas objetos que herdassem de Sleeper poderiam ser utilizados no AlarmClock. Se o relógio for um applet ele deve herdar da classe applet. Como Java não permite herança múltipla não seria possível tornar o relógio um dorminhoco (Sleeper). Interfaces provem Herança Múltipla? Podem ser encarados como um paliativo, mas são coisas diferentes: uma classe herda apenas constantes de uma interface; uma classe não pode herdar implementações de uma interface. A hierarquia de interfaces é independente da hierarquia de classes. Classes que implementam a mesma interface podem ou não estar relacionadas na hierarquia. Java permite herança múltipla de interfaces. Para que usar Interfaces? Use interfaces para definir protocolos de comportamento que possam ser implementados em qualquer lugar na hierarquia de classes. Interfaces são úteis para: capturar similaridades entre classes não relacionadas; declarar métodos que uma ou mais classes devem inevitavelmente implementar; revelar interfaces sem revelar os objetos que a implementam (útil na venda de pacotes de componentes). Exercícios de Hoje 1. Compile e explique cada uma das linhas do exemplo da seção "Herança e polimorfismo"atuando juntos para reuso de código. Cite em que linhas o polimorfismo está presente. Utilize a classe Figura que seja abstrata. incremente a classe figura com métodos tais como mover, posicionar, aumentar e diminuir. crie mais de um construtor com parâmetros. 2. Compile e execute o código disponível no pacote Alarme.zip. Tente explicar a função da interface Sleeper. Explique como o polimorfismo age sobre a função wakeUp() de Sleeper. 3. Baseado no que foi dito. Dê um exemplo da possível utilização de classes abstratas e interfaces em Programação Orientada para Objetos. Entregue apenas as respostas num único documento TXT ou DOC.
Compartilhar