Classe abstrata e Interface em Java

Alguém pode me da uma explicação ou mandar algum artigo/video sobre Classe abstrata e Interface (principalmente interface), não conseguir entender a explicação do professor, e agora estou todo enrolado para fazer as atividades.

1 curtida

Vc pode entender as duas como quaaaase a mesma coisa. Uma classe abstrata é um modelo mais abstrato de algo mais concreto. Vou te dar um exemplo de uma abstração que eu sempre gosto de usar. Imagine que você precisa modelar uma hierarquia de formas geométricas de duas dimensões usando classes. Quais formas são mais comuns?

  • Quadrado
  • Triângulo
  • Retângulo
  • Círculo

Concorda? Muito bem. Essas formas existem por si próprias, então você pode imaginar que podem existir instâncias dessas formas, certo? O que eu quero dizer com instância é que você pode ter um quadrado de 5cm de lado, outro quadrado com 10m de lado, um círculo de 2mm de raio etc. Tudo bem até aqui? Fazendo um paralelo com o mundo orientado a objetos, vc pode associar tais formas com sua respectiva classe e cada instância com um objeto criado a partir da classe, que por sinal, também é a terminologia que usamos para chamar os objetos.

Agora vamos pensar nas características dessas formas. O que há de comum entre um quadrado, um retângulo e um triângulo (vamos deixar o círculo de fora por enquanto)? Cada um, independente da instância, tem uma quantidade de lados concorda? Pensando na disposição dos mesmos no plano cartesiano, podemos pensar nas coordenadas de cada um dos seus vértices concorda? Ou então poderíamos pensar em termos de um ponto de origem de cada forma e representar os outros vértices com base na largura e na altura de cada uma. Muito bem, se essas formas possuem características em comum, será que não seria legal se pudéssemos ter uma classe mais geral/abstrata que modelasse tais características? Imagine uma classe chamada Forma. Essa classe serviria como um modelo para dizer que toda classe que herda dela é uma forma geométrica, mas não faz muito sentido vc ter uma instância direta de forma, pois ela é usada como um modelo para outras classes. Apesar disso, essa classe ainda poderia ter comportamentos implementados nela, mesmo ela não sendo candidata a ter instâncias criadas. Se vc tem uma classe que pode servir de modelo, contendo atributos comuns, operações implementadas e outras que precisam ser implementadas você provavelmente tem uma classe abstrata. Eu vou fazer o exemplo em código para ficar mais claro, vamos só terminar a “teoria”, apresentada aqui de informalmente.

Muito bem, agora vc tem mais ou menos ideia do que é uma classe abstrata. Normalmente quando estamos modelando nosso software usando orientação a objetos, todas as nossas classes do modelo de negócios, sejam abstratas ou concretas, são nomeadas com substantivos, pois elas são modelos para se criar objetos que representam coisas do mundo real. Uma forma, um quadrado, um carro, uma roda, uma casa, uma pessoa, um cliente, uma nota fiscal etc.

Quando passamos a pensar nas interfaces, vc pode entendê-las, pelo menos por agora, como classes 100% abstratas. Hoje em dia em Java isso não é mais totalmente verdade, mas vamos assumir que sim para não criar (mais) confusão. Ao criar interfaces, normalmente os nomes que damos a elas são adjetivos, pois vão adicionar comportamentos ou características às nossas classes. Você pode enxergar essa adjetivação na forma de um contrato que precisa obrigatoriamente ser seguido pela primeira classe concreta que implementar uma ou mais interfaces.

Em Java há algumas restrições do ponto de vista de herança ao envolvermos classes e interfaces. Uma classe só pode herdar diretamente de uma classe, seja essa concreta ou abstrata, mas pode implementar uma ou mais interfaces. A ideia é, uma classe pode também ser uma outra coisa e pode ter uma ou mais características trazidas de uma interface.

Voltando ao nosso exemplo das formas, imagine que você usará essas classes para implementar um programa de desenho. Suas formas podem fazer sentido em serem apenar repositórios para dados internos que controlam a execução do programa, por exemplo, um retângulo que não aparecerá na tela, mas será usado para internamente representar o corte de uma imagem ou então formas que serão de fato desenhadas na tela. Se eu posso ter formas que são desenháveis, que tal ter uma interface que represente esse comportamento e que, quando uma classe implementar essa interface, ela será desenhável no programa?

Enfim, vou tentar te mostrar tudo isso com um exemplo:

package modelo;

import java.awt.Color;

/**
 * Classe abstrata forma. Toda forma é desenhável
 * e interceptável. Os contratos das interfaces são implementados
 * nas classes concretas que herdam de forma.
 * 
 * @author David Buzatto
 */
public abstract class Forma implements Desenhavel, Interceptavel {
    
    // x e y representam a coordenada central da forma
    private int x;
    private int y;
    
    // largura e altura da forma
    private int largura;
    private int altura;
    
    // cor da forma
    private Color cor;

    public int getX() {
        return x;
    }

    public void setX( int x ) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY( int y ) {
        this.y = y;
    }

    public int getLargura() {
        return largura;
    }

    public void setLargura( int largura ) {
        this.largura = largura;
    }

    public int getAltura() {
        return altura;
    }

    public void setAltura( int altura ) {
        this.altura = altura;
    }

    public Color getCor() {
        return cor;
    }

    public void setCor( Color cor ) {
        this.cor = cor;
    }
    
    /**
     * Calcula a área da forma.
     * Cada classe concreta terá que implementar esse método.
     * 
     * @return A área da forma.
     */
    public abstract int calcularArea();
    
}



package modelo;

import java.awt.Graphics2D;

/**
 * A interface Desenhavel fornece o contrato
 * para tornar uma classe desenhável no contexto
 * do Java2D.
 * 
 * @author David Buzatto
 */
public interface Desenhavel {
    
    /**
     * Realiza a operação de desenho de uma forma no
     * contexto do Java2D.
     * 
     * Por padrão os métodos definidos em uma interface
     * são públicos e abstratos. A definição desse método
     * corresponde implicitamente à
     * public abstract void desenhar( Graphics2D g2d )
     * 
     * @param g2d contexto gráfico bidimensional. Recomenda-se ao
     * implementador que seja feita uma cópia do contexto passado
     * e sua posterior liberação após a realização das operações
     * gráficas.
     */
    void desenhar( Graphics2D g2d );
    
}



package modelo;

/**
 * A interface Interceptavel fornece o contrato
 * para tornar uma classe interceptavel por uma
 * coordenada.
 * 
 * @author David Buzatto
 */
public interface Interceptavel {
    
    /**
     * Verifica se uma coordenada intercepta a forma
     * implementadora, ou seja, se x e y estão inscritos
     * na forma.
     * 
     * @param x componente x da coordenada
     * @param y componente y da coordenada
     * @return verdadeiro caso x e y estejam inscritos na forma, falso
     * caso contrário
     */
    boolean intercepta( int x, int y );
    
}



package modelo;

import java.awt.Graphics2D;

/**
 * Uma classe concreta de Forma que representa
 * um retângulo.
 * 
 * @author David Buzatto
 */
public class Retangulo extends Forma {

    /*
     * Implementação do método desenhar da interface Desenhavel
     */
    @Override
    public void desenhar( Graphics2D g2d ) {
        
        // cópia do contexto atual
        g2d = (Graphics2D) g2d.create();
        
        // como o x e o y da nossa forma é o ponto central
        // precisamos calcular o x e o y esperados pelo método
        // drawRect, que é a coordenada do canto superior esquerdo
        // da forma
        
        int xIni = getX() - getLargura() / 2;
        int yIni = getY() - getAltura() / 2;
        
        // configura a cor do "pincel" do contexto gráfico
        g2d.setColor( getCor() );
        
        // desenha o retângulo usando a coordenada calculada
        // e a largura e a altura
        g2d.drawRect( xIni, yIni, getLargura(), getAltura() );
        
        // desenha uma string com a área da forma no centro
        g2d.drawString( "Área: " + calcularArea(), getX(), getY() );
        
        // liberação
        g2d.dispose();
        
    }

    /*
     * Implementação do método intercepta da interface Interceptavel
     */
    @Override
    public boolean intercepta( int x, int y ) {
        
        int xIni = getX() - getLargura() / 2;
        int yIni = getY() - getAltura() / 2;
        int xFim = getX() + getLargura() / 2;
        int yFim = getY() + getAltura() / 2;
        
        return x >= xIni && x <= xFim &&
               y >= yIni && y <= yFim;
        
    }

    @Override
    public int calcularArea() {
        return getLargura() * getAltura();
    }
    
}



package modelo;

/**
 * Uma classe concreta que representa um quadrado.
 * Um quadrado é um tipo especial de retângulo onde
 * todos os lados são congruentes, ou seja, é um 
 * losango e um retângulo ao mesmo tempo.
 * 
 * @author David Buzatto
 */
public class Quadrado extends Retangulo {

    // o método desenhar não precisa ser implementado
    // pois o herdado basta

    // idem ao intercepta
    
    // o que valos resolver aqui é garantir que
    // a configuração da largura e da altura sejam
    // sempre iguais, sobrescrevendo os métodos
    // setLargura e setAltura
    
    @Override
    public void setLargura( int largura ) {
        super.setLargura( largura );
        super.setAltura( largura );
    }
    
    @Override
    public void setAltura( int altura ) {
        super.setLargura( altura );
        super.setAltura( altura );
    }
    
}



package modelo;

import java.awt.Graphics2D;

/**
 * Uma classe concreta de Forma que representa
 * um círculo.
 * 
 * @author David Buzatto
 */
public class Circulo extends Forma {

    // um círculo possui um raio
    // que corresponderá a largura/2 e altura/2
    private int raio;
    
    /*
     * Implementação do método desenhar da interface Desenhavel
     */
    @Override
    public void desenhar( Graphics2D g2d ) {
        
        // cópia do contexto atual
        g2d = (Graphics2D) g2d.create();
        
        // como o x e o y da nossa forma é o ponto central
        // precisamos calcular o x e o y esperados pelo método
        // drawOval, que é a coordenada do canto superior esquerdo
        // da forma
        
        int xIni = getX() - raio;
        int yIni = getY() - raio;
        
        // configura a cor do "pincel" do contexto gráfico
        g2d.setColor( getCor() );
        
        // desenha o retângulo usando a coordenada calculada
        // e o raio (poderia usar largura e altura, pois
        // garantimos a compatibilidade nas implementações
        // abaixo
        g2d.drawOval( xIni, yIni, raio * 2, raio * 2 );
        
        // desenha uma string com a área da forma no centro
        g2d.drawString( "Área: " + calcularArea(), getX(), getY() );
        
        // liberação
        g2d.dispose();
        
    }

/*
     * Implementação do método intercepta da interface Interceptavel
     */
    @Override
    public boolean intercepta( int x, int y ) {
        
        // se a distância entre o centro e a coordenada passada
        // (teorema de pitágoras) for menor ou igual ao raio
        return ( (int) Math.hypot( x - getX(), y - getY() ) ) <= raio;
        
    }

    @Override
    public int calcularArea() {
        return (int) (Math.PI * raio * raio);
    }

    public int getRaio() {
        return raio;
    }

    public void setRaio( int raio ) {
        
        this.raio = raio;
        
        // mantendo compatibilidade com a API de Forma
        setLargura( raio * 2 );
        setAltura( raio * 2 );
        
    }
    
    @Override
    public void setLargura( int largura ) {
        
        this.raio = largura / 2;
        
        super.setLargura( largura );
        super.setAltura( largura );
        
    }
    
    @Override
    public void setAltura( int altura ) {
        
        this.raio = altura / 2;
        
        super.setLargura( altura );
        super.setAltura( altura );
        
    }
    
}



package exemplodesenho;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import modelo.Circulo;
import modelo.Quadrado;
import modelo.Retangulo;

/**
 * Janela de testes.
 * 
 * @author David Buzatto
 */
public class Janela extends JFrame {
    
    public Janela() {
        
        setTitle( "Formas" );
        setSize( 800, 800 );
        setLocationRelativeTo( null );
        setDefaultCloseOperation( EXIT_ON_CLOSE );
        
    }

    @Override
    public void paint( Graphics g ) {
        
        super.paint( g );
        
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setRenderingHint( 
                RenderingHints.KEY_ANTIALIASING, 
                RenderingHints.VALUE_ANTIALIAS_ON );
        
        g2d.setColor( Color.WHITE );
        g2d.fillRect( 0, 0, getWidth(), getHeight() );
        
        Retangulo r = new Retangulo();
        r.setX( 400 );
        r.setY( 200 );
        r.setLargura( 400 );
        r.setAltura( 150 );
        r.setCor( Color.RED );
        r.desenhar( g2d );
        
        Quadrado q = new Quadrado();
        q.setX( 400 );
        q.setY( 400 );
        q.setLargura( 150 );
        q.setAltura( 200 );
        q.setCor( Color.BLUE );
        q.desenhar( g2d );
        
        Circulo c = new Circulo();
        c.setX( 600 );
        c.setY( 600 );
        c.setRaio( 100 );
        c.setCor( Color.GREEN.darker() );
        c.desenhar( g2d );
        
        g2d.dispose();
        
    }
    
    public static void main( String args[] ) {
        
        EventQueue.invokeLater( new Runnable() {
            @Override
            public void run() {
                new Janela().setVisible( true );
            }
        });
        
    }
    
}

Ao executar, o resultado será:

Existem vários outros detalhes, mas um início seria isso para vc ter uma ideia. Atualizei o exemplo, pq o método intercepta da classe Circulo estava “fraco”. Um projeto do NetBeans com o código vc pode ver abaixo.

ExemploDesenho.zip (23,2,KB)

4 curtidas