Classe Abstrata

Classes Abstratas

Em Java, temos um tipo especial de classe chamado classe abstrata. Este tipo de classe possui uma característica muito específica, que é o de não permitir que novos objetos sejam instanciados a partir desta classe. Por este motivo, as classes abstratas possuem o único propósito de servirem como super classes a outras classes do Java.

Em Java definimos uma classe como abstrata utilizando a palavra-chave abstract na declaração da classe. No vídeo a seguir mostramos passo a passo como funciona a classe abstrata:

Vamos praticar mais um pouco, veja este outro exemplo do uso de classe abstrata:

package material.abstrata;

/**
 * Classe Abstrata representando uma Pessoa.
 */
public abstract class Pessoa {
  private String nome;

  public Pessoa(String nome) {
    this.nome = nome;
  }

  public String getNome() {
    return nome;
  }
  public void setNome(String nome) {
    this.nome = nome;
  }
}

Na linha 6 estamos criando uma classe abstrata através da palavra-chave abstract.

Uma classe abstrata é desenvolvida para representar entidades e conceitos abstratos, sendo utilizada como uma classe pai, pois não pode ser instanciada. Ela define um modelo (template) para uma funcionalidade e fornece uma implementação incompleta - a parte genérica dessa funcionalidade - que é compartilhada por um grupo de classes derivadas. Cada uma das classes derivadas completa a funcionalidade da classe abstrata adicionando um comportamento específico.

Uma classe abstrata normalmente possui métodos abstratos. Esses métodos são implementados nas suas classes derivadas concretas com o objetivo de definir o comportamento específico. O método abstrato define apenas a assinatura do método e, portanto, não contém código assim como feito nas Interfaces.

Uma classe abstrata pode também possuir atributos e métodos implementados, componentes estes que estarão integralmente acessíveis nas subclasses, a menos que o mesmo seja do tipo private.

Como todo método abstrato precisa ser implementado pela classe filha, então não pode ser private, pois não seria visível na subclasse.

Neste exemplo, criaremos uma classe abstrata chamada Tela. Nesta classe implementaremos alguns métodos que devem ser utilizados por todas as telas do computador de bordo de um veículo, como: setTitulo() para informar o título da tela e imprimir(), que imprime as informações na tela.

Nesta classe definiremos também a assinatura de um método abstrato chamado obterInformacao(), que deve ser implementado pelas classes filhas.

package material.abstrata;

/**
 * Classe abstrata que possui os métodos básicos para
 * as telas do computador de bordo de um veiculo.
 */
public abstract class Tela {
  private String titulo;

  public void setTitulo(String titulo) {
    this.titulo = titulo;
  }

  public abstract String obterInformacao();

  public void imprimir() {
    System.out.println(this.titulo);
    System.out.println("\t" + obterInformacao());
  }
}

Agora criaremos a classe TelaKilometragem, que será uma subclasse da classe abstrata Tela. Esta classe será utilizada para mostrar a km atual percorrida pelo veículo.

package material.abstrata;

/**
 * Tela que mostra a kilometragem percorrida por um veiculo.
 */
public class TelaKilometragem extends Tela {
  /* Atributo que guarda o valor da km atual do veiculo. */
  int km = 0;

  /**
   * Construtor que iniciliza o titulo da tela.
   */
  public TelaKilometragem() {
    /* Atribui o valor do titulo desta tela. */
    super.setTitulo("Km Atual");
  }

  /**
   * Implementa o método abstrato da classe Tela,
   * neste método buscamos a km atual do veiculo.
   * 
   * @return Texto com a km atual.
   */
  @Override
  public String obterInformacao() {
    km += 10;
    return String.valueOf(km) + " km";
  }
}

Vamos, agora, criar uma classe para testar a nossa aplicação. Neste teste, criaremos um objeto da classe TelaKilometragem e chamar o método imprimir(), que esta classe herdou da classe Tela.

package material.abstrata;

/**
 * Classe utilizada para testar a Tela Kilometragem.
 */
public class TesteTelaKm {
  public static void main(String[] args) {
    TelaKilometragem tk = new TelaKilometragem();
    tk.imprimir();

    System.out.println("\n------------\n");

    tk.imprimir();
  }
}

Quando definimos Tela tk = new TelaKilometragem(), estamos guardando um objeto do tipo TelaKilometragem dentro de uma variável do tipo Tela. Podemos fazer isso porque toda TelaKilometragem é uma subclasse de Tela.

Quando chamamos o método tk.imprimir(), estamos chamando o método que foi implementado na classe abstrata Tela, mas note que o método imprimir() usa o método obterInformacao(), que foi implementado na classe TelaKilometragem. Em tempo de execução, a JVM verifica o tipo do objeto e chama o método que foi implementado nele, isto chama-se Polimorfismo.

Temos a seguinte saída no console:

Km Atual
	10 km

----------

Km Atual
	20 km

Segue abaixo um quadro comparativo entre Interface e Classe Abstrata:

Classe Abstrata Interface
Pode possui atributos de instância Possui apenas constantes
Possui métodos de diversas visibilidade e métodos implementados ou abstratos Todos os métodos são public e abstract
É estendida por classes (sub-classes) É implementada por classes
Uma subclasse só pode estender uma única classe abstrata Uma classe pode implementar mais de uma interface
Hierarquia de herança com outras classes abstratas Hierarquia de herança com outras 
interfaces