Classes Abstratas e Interfaces Java Básico Classes Abstratas e Interfaces Marco Antonio, Arquiteto de Software – TJDF ma@marcoreis.net Atualizado em Novembro/2008
Classe abstrata Recurso avançado da OO Não pode ser instanciada Deve ser instanciada através de uma subclasse concreta Permite modelar com mais precisão as regras de negócio Refinamento do projeto O programador fica obrigado a utilizar as regras definidas pelo projetista Pode ter a implementação para os métodos
Interface Funciona como um contrato Não pode ser instanciada Pode ser instanciada através de uma classe que a implemente Obriga o programador a usar os métodos já definidos Não tem implementação, apenas a assinatura dos métodos Muito útil para diminuir o acoplamento entre os vários módulos do sistema
Classe abstrata x Interface Não existe uma regra geral que defina quando usar classes abstratas ou interfaces O nível de refinamento deve ser definido antes da implementação, o que diminui o risco de entrar num ciclo de refinamentos sem fim
Dica Quando haja a possibilidade de reutilização de código, prefira classe abstrata Se o código reaproveitado é muito pequeno, talvez seja mais interessante uma interface
Exemplo de classe abstrata Vamos utilizar a classe Musico para ilustrar o conceito de classe abstrata Baterista, Guitarrista e Baixista são nossas outras classes do modelo Analise o relacionamento entre elas
Musico package com.javabasico.classesabstratas; public abstract class Musico { public abstract String getNomeDoInstrumento(); public void afinarInstrumento() { System.out.println("Estou afinando o/a " + getNomeDoInstrumento()); }
Baixista package com.javabasico.classesabstratas; public class Baixista extends Musico { public String getNomeDoInstrumento() { return "Baixo"; }
Guitarrista package com.javabasico.classesabstratas; public class Guitarrista extends Musico { public String getNomeDoInstrumento() { return "Guitarra"; }
Baterista package com.javabasico.classesabstratas; public class Baterista extends Musico { public String getNomeDoInstrumento() { return "Bateria"; }
TesteDoMusico package com.javabasico.classesabstratas; public class TesteDoMusico { public static void main(String[] args) { Musico baixista = new Baixista(); baixista.afinarInstrumento(); Musico batera = new Baterista(); batera.afinarInstrumento(); Musico guitarrista = new Guitarrista(); guitarrista.afinarInstrumento(); }
Exemplo de interface Vamos utilizar a interface Veiculo no nosso exemplo Além desta, também teremos Carro, Moto e Caminhao
Veiculo package com.javabasico.interfaces; public interface Veiculo { public int getQuantidadeDePneus(); public boolean isVeiculoDePasseio(); public int getPotencia(); }
Carro package com.javabasico.interfaces; public class Carro implements Veiculo { public int getQuantidadeDePneus() { return 4; } public boolean isVeiculoDePasseio() { return true; public int getPotencia() { return 80;
Moto package com.javabasico.interfaces; public class Moto implements Veiculo{ public int getQuantidadeDePneus() { return 2; } public boolean isVeiculoDePasseio() { return true; public int getPotencia() { return 30;
Caminhao package com.javabasico.interfaces; public class Caminhao implements Veiculo { public int getQuantidadeDePneus() { return 6; } public boolean isVeiculoDePasseio() { return false; public int getPotencia() { return 130;
TesteDoVeiculo package com.javabasico.interfaces; public class TesteDoVeiculo { public static void main(String[] args) { Veiculo minhaMoto = new Veiculo(); }
Qual o problema? Qual o problema da classe de teste? Simples: uma interface não pode ser instanciada new Veiculo(); Não existe, no modelo anterior, um objeto do tipo Veiculo (o veículo é apenas conceitual) Existem apenas Carros, Motos e Caminhoes Toda interface deve ter uma classe que a implemente, caso contrário, não faz sentido esse tipo de refinamento
TesteDoVeiculo package com.javabasico.interfaces; public class TesteDoVeiculo { public static void main(String[] args) { Veiculo minhaMoto = new Moto(); System.out.println("Quantidade de cavalos da moto:" + minhaMoto.getPotencia()); Veiculo meuCarro = new Carro(); System.out.println("Quantidade de pneus do carro: " + meuCarro.getQuantidadeDePneus()); Veiculo meuCaminhao = new Caminhao(); System.out.println("O caminhão é veículo de passeio? " + meuCaminhao.isVeiculoDePasseio()); }
Exemplo Web Vamos imaginar a seguinte hierarquia de classes: ControleHTML CaixaDeTexto CaixaDeChecagem Outros componentes HTML O ControleHTML terá os atributos comuns a todos os controles web (nome, valor) e deverá ser definido como abstract, porque o método getCodigoHTML não tem implementação. No momento de gerar a página HTML iremos instanciar o componente concreto (caixas de texto, comboboxes, caixas de checagem, botoes, etc). Para cada componente, teremos uma saída diferente para o método getCodigoHTML.
ControleHTML package com.javabasico.classesabstratas; public abstract class ControleHTML { private String nome; private String valor; public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; public String getValor() { return valor; public void setValor(String valor) { this.valor = valor; public abstract String getCodigoHTML();
CaixaDeChecagem package com.javabasico.classesabstratas; public class CaixaDeChecagem extends ControleHTML { public String getCodigoHTML() { StringBuilder html = new StringBuilder(); html.append("<input type=\"checkbox\""); html.append("name="); html.append(getNome()); html.append("\"value=\""); html.append(getValor()); html.append("\">"); return html.toString(); }
CaixaDeTexto package com.javabasico.classesabstratas; public class CaixaDeTexto extends ControleHTML { public String getCodigoHTML() { StringBuilder html = new StringBuilder(); html.append("<input type=\"text\""); html.append("name=\""); html.append(getNome()); html.append("\" value=\""); html.append(getValor()); html.append("\" >"); return html.toString(); }
PaginaWeb package com.javabasico.classesabstratas; public class PaginaWeb { public static void main(String[] args) { ControleHTML caixaDeTexto = new CaixaDeTexto(); caixaDeTexto.setNome("txtNome"); caixaDeTexto.setValor("Marco"); ControleHTML caixaDeChecagem = new CaixaDeChecagem(); caixaDeChecagem.setNome("chkUsuarioCadastrado"); caixaDeChecagem.setValor("1"); System.out.println(caixaDeTexto.getCodigoHTML()); System.out.println(caixaDeChecagem.getCodigoHTML()); }
Exemplo com interfaces No próximo exemplo teremos as formas geométricas conforme a hierarquia: FormaGeometrica Triangulo Quadrado Retangulo Etc... A interface principal terá os métodos desenhar e apagar, claro, sem a implementação, que será feita nas classes a seguir.
FormaGeometrica package com.javabasico.interfaces; public interface FormaGeometrica { public void desenhar(); public void apagar(); }
Triangulo package com.javabasico.interfaces; public class Triangulo implements FormaGeometrica { public void apagar() { System.out.println("Estou apagando o triângulo..."); } public void desenhar() { System.out.println("Estou desenhando um triângulo...");
Quadrado package com.javabasico.interfaces; public class Quadrado implements FormaGeometrica { public void apagar() { System.out.println("Estou apagando o quadrado."); } public void desenhar() { System.out.println("Estou desenhando um quadrado.");
TesteFormasGeometricas package com.javabasico.interfaces; public class TesteFormasGeometricas { public static void main(String[] args) { FormaGeometrica forma = new Triangulo(); forma.desenhar(); forma.apagar(); }
Ligação dinâmica Devemos observar que criamos um objeto do tipo FormaGeometrica e que dinamicamente a linguagem trocou para a classe Triangulo. Esse comportamento é verificado nas interfaces e nas classes.
TesteFormasGeometricas package com.javabasico.interfaces; public class TesteFormasGeometricas { public static void main(String[] args) { FormaGeometrica forma = new Triangulo(); forma.desenhar(); forma.apagar(); // forma = new Quadrado(); }
Sua vez Crie mais duas formas geométicas e teste tudo na nossa classe.
Curiosidades O mesmo objeto (forma) teve dois comportamentos diferentes, executando métodos em classes diferentes. No próximo exemplo teremos mais uma curiosidade, passando uma interface como parâmetro. O SoftwareGrafico irá aceitar somente implementações de FormaGeometrica. Para testar, crie a classe Elipse com os mesmos métodos da FormaGeometrica, mas não implemente a interface. Tente passá-la como parâmetro. O que acontece?
SoftwareGrafico package com.javabasico.interfaces; public class SoftwareGrafico { public void adicionarForma(FormaGeometrica forma) { forma.desenhar(); } public void removerForma(FormaGeometrica forma) { forma.apagar();
TesteSoftwareGrafico package com.javabasico.interfaces; public class TesteSoftwareGrafico { public static void main(String[] args) { SoftwareGrafico photoshop = new SoftwareGrafico(); Quadrado umQuadrado = new Quadrado(); photoshop.adicionarForma(umQuadrado); // Triangulo umTriangulo = new Triangulo(); photoshop.adicionarForma(umTriangulo); photoshop.removerForma(umTriangulo); photoshop.removerForma(umQuadrado); }
Dúvidas?
Impressora package com.javabasico.classesabstratas; public interface Impressora { void imprimir(); void imprimir(String texto); }
Lexmark package com.javabasico.classesabstratas; public class Lexmark implements Impressora { @Override public void imprimir() { System.out.println("Estou imprimindo na Lexmark..."); } public void imprimir(String texto) { System.out.println("Estou imprimindo o texto: " + texto + "na Lexmark.");
Teste package com.javabasico.classesabstratas; public class TesteDaImpressora { public static void main(String args[]) { Impressora l = new Lexmark(); l.imprimir(); l.imprimir("Alguma coisa"); }
Classes Anônimas package com.javabasico.classesabstratas; public class TesteImpressoraAnonima { public static void main(String[] args) { Impressora i = new Impressora() { @Override public void imprimir() { System.out.println("Imprimindo pela classe anonima"); } public void imprimir(String texto) { System.out.println("Classe anonima: " + texto); }; i.imprimir(); i.imprimir("Algum texto");
Exercício Cria uma classe para representar as peças de um veículo. Essa deverá ser uma classe genérica e abstrata (ou interface). Em seguida crie as peças, que serão subclasses (porta, vidro, lanterna, retrovisor). Por fim, crie uma classe de teste e monte um carro com essas peças.