HERANÇA

 

Herança é um dos principais fundamentos em POO (Programação Orientada a Objeto), além de deixar o código mais limpo, a herança permite que uma classe herde de outra classe, todos os comportamentos e atributos. Através dela a classe possui imediatamente todas as funcionalidades de uma classe já existente.

 

Nesta classe já existente, também chamada de classe pai ou superclasse, se declaram os atributos e métodos comuns a todos os sub-níveis (generalização) na hierarquia de herança, ou seja, às subclasses. Nas subclasses, ou classes derivadas, são definidas suas particularidades (especialização), que herdam todas suas características provenientes da classe pai.

 
 

Por exemplo: pode-se observar a relação Pai (superclasse de filho e subclasse de Pessoa), pois Pai herda as características de Pessoa diretamente. A subclasse Filho sempre herda alguma característica do pai, seja o nome, aspectos físicos (boca, nariz, etc.) ou a personalidade. Mas ambos, Pai e Filho, herdam características de uma outra superclasse chamada Pessoa, que por sua vez herda diretamente os atributos da superclasse comum na programação em Java (Object). Esta superclasse, denominada Object, é a mais geral da hierarquia e define o comportamento e os atributos herdados por todas as classes da linguagem Java.

 

A superclasse Java é uma classe que fornece métodos e atributos para uma subclasse, ou seja, uma superclasse é a classe base para suas subclasses. As subclasses são as classes que herdam todos os comportamentos e métodos da classe base (superclasse), sendo também chamadas de classes derivadas.

 
 

A herança é útil na especialização de uma subclasse, pois a subclasse pode implementar métodos específicos aos aspectos que atendam a necessidade somente de uma subclasse.

 

A palavra reservada extends indica que uma classe estende outra, isto é, uma subclasse herda os atributos e métodos de sua superclasse e aumenta seus recursos para atender as necessidades mais específicas, geralmente, existentes nas subclasses. Um destaque importante nesta extensão faz referência ao qualificador de acesso private que não permite a herança de atributos e métodos entre a superclasse e suas subclasses. No exemplo a seguir é mostrada a utilização da extends.

 
/**
*Síntese
*  Atributos: nome e olhos
*  Métodos: nome(String), olhos(String)
*/

public class Pessoa {

    protected String nome, olhos;    // atributos protegidos desta classe
    
    public void nome(String parametroNome) {
    	nome = parametroNome;
    }
    
    public void olhos(String parametroOlhos) {
 	  	olhos = parametroOlhos;
    }

}
 

A classe Pessoa consiste em uma superclasse direta de Pai e indireta de Filho. Esta classe possui 2 atributos (nome, olhos) e 2 métodos capazes de atribuir novos valores a estes atributos.

 
/**
*Síntese
*  Atributos: nome, olhos e personalidade
*  Métodos: nome(String), olhos(String) e personalidade(String)
*/

public class Pai extends Pessoa{
    protected String personalidade;
    
    public void personalidade(String persona) {
       personalidade = persona;
    }
}
 

Na codificação anterior, a classe Pai utiliza a instrução extends para receber a herança dos recursos existentes na classe Pessoa. Além disso, Pai também declara um novo atributo (personalidade) e um método específico (personalidade) para suas necessidades de implementação.

 
/**
*Síntese
*  Atributos: nome, olhos, personalidade e sexo
*  Métodos: nome(String), olhos(String), personalidade(String) e sexo(String)
*/

public class Filho extends Pai{
    protected String sexo;
    
    public void sexo(String sex) {
       sexo = sex;
    }
}
 

Na classe Filho é usada a instrução extends que a permite receber diretamente todos os atributos e métodos da classe Pai e indiretamente da classe Pessoa. Completando as necessidades desta subclasse (Filho) ainda são definidos um novo atributo e um método coerente com suas características específicas.

 
/**
* Síntese:
*    Objetivo: cadastrar pai e filho
*    Entrada: sem entrada (só atribuições)
*    Saída: nome do pai e filho, personalidade, cor dos olhos do pai e filho 
*/

public class Familia {
    
    public static void main(String [] args){    
        Pai pai = new Pai();
        Filho filho = new Filho(); 
        
        // atribuições para objeto pai
        pai.nome("João Fernando");
        pai.olhos("Azuis");
        pai.personalidade("calmo");
        
        // atribuições para objeto filho
        filho.nome(pai.nome + " Junior");
        filho.olhos(pai.olhos);
        filho.sexo("Masculino");        
        // atribuições para objeto filho

        // Mostra os valores armazenados usando a herança
        System.out.println("PAI\n===");
        System.out.println("Nome do pai: " + pai.nome);
        System.out.println("Os olhos do pai: " + pai.olhos);
        System.out.println("Personalidade do pai: " + pai.personalidade);
        System.out.println("\n\n\nFILHO\n=====");
        System.out.println("Nome do Filho: " + filho.nome);
        System.out.print("Os olhos do filho: " + filho.olhos + "\t");
        System.out.println("(iguais ao do pai)" );
        System.out.println("Sexo do filho: " + filho.sexo);
    }
}
 

A classe Familia possui o método main, responsável pela execução da aplicação (programa) Java, instanciando pai e filho. Neste método estão presentes todos os recursos (atributos e métodos) da classe Pai e Filho, por meio da herança, além dos recursos de Pessoa. Após a criação das instâncias (objetos) são acionados os respectivos métodos que possibilitaram a atribuição dos valores aos atributos existentes nestas classes. Ao final deste exemplo de código são mostrados os valores que estão armazenados nestes atributos em memória.

 

A execução do programa anterior apresentaria a saída de dados na console como representado na figura a seguir:

 
   
Construtores e Herança
 

Em Java, construtores, também chamados de métodos contrutores, não são herdados. Com isso, os construtores de uma subclasse incluem as chamadas aos construtores de sua superclasse, através da palavra reservada super.

 
 

Observe no exemplo a seguir a utilização da instrução super nas classes Animal e Macaco:

 
/**
*Síntese
*  Atributos: patas
*  Métodos: Animal(int), qtdPatas(int), caminha(), come(boolean);
*/

public class Animal {

    // atributo
    protected int patas;
    
    // construtor
    Animal(int pata){
       qtdPatas(pata);
    }
    
    public void qtdPatas(int pata){
       patas = pata;
    }
    
    public void caminha() {
       boolean caminha = false;
       if (patas > 0)
          caminha = true;
       if(caminha)
          System.out.println("sim");
       else
          System.out.println("não");
    }
    
    public void come(boolean comia){
        if (comia)
           System.out.println("sim");
        else
           System.out.println("não");
    }
}

/**
*Síntese
*   Objetivo: mostrar características de um macaco
*   Entrada: sem entrada (só atribuições)
*   Saída: características do macaco
*/

public class Macaco extends Animal {

    Macaco(int pata, int pernas) {   
        super(4);//aciona explícitamente o construtor da super classe desta classe     
        int anda = pernas;
        System.out.println("O macaco anda com " + anda + " pernas");
    }
    
    public void come() {        
        boolean come = false;
        if(come)
           System.out.println("sim");
        else
           System.out.println("não");
    }
    
    public static void main(String [] Args){
        Macaco macaco = new Macaco(4,2);
        System.out.println("Quantidade de patas: " + macaco.patas);
        System.out.print("O macaco está caminhando? ");
        macaco.caminha();
        System.out.print("O macaco está comendo? ");
        macaco.come();   // ele chama o método da classe Macaco
        System.out.print("O macaco estava comendo? ");
        macaco.come(true);  // ele chama o método da classe Animal   
    }
}
 

O construtor da classe Animal recebe um argumento, ao passo que a subclasse Macaco não herda tal construtor. Declara-se assim, o construtor da classe Macaco, no qual ocorre uma invocação ao construtor de sua superclasse, usando super, fazendo com que a classe Macaco chame o construtor da classe Animal recebendo os atributos e métodos da classe Animal.

 
 

As subclasses asseguram o comportamento da sua superclasse, ou seja, podem acrescentar atributos e métodos, além de sobrepor métodos herdados de suas classes superiores. Essa sobreposição é feita quando algum método herdado da superclasse é desnecessário para a subclasse, podendo assim ser redefinido ou sobrescrito na própria subclasse.

 

O programa anterior, que envolve as classes Animal e Macaco, mostraria em sua console os dados representados na figura a seguir:

 
 

Para este exemplo a subclasse Macaco está implementando o método come(), herdado da superclasse Animal, de modo que as definições que prevalecerão no acionamento deste método na classe Macaco serão sobrepostas às definições provenientes da superclasse Animal. Um outro exemplo poderia abordar que todos animais possuem patas, onde o macaco possui 4 patas, mas anda somente com duas. Porém, a zebra também possui 4 patas, mas para andar precisa de todas elas (4 patas). Isso indica que apesar dos animais possuírem patas, a funcionalidade destas patas para cada um é diferente, ou seja, específica às suas necessidades e características.

 
 

No caso da sobreposição de métodos, a implementação a ser executada obedece a hierarquia de classes, de baixo para cima. No exemplo, primeiro a classe Macaco é verificada, em seguida (e se necessário), a classe Animal. Esta situação de análise corresponde a precedência para acionamento de um método, como é comum no uso das variáveis locais ou globais em um programa que possua o mesmo identificador. A diferença no uso de uma ou outra variável será definida pela precedência da local sobre a global.

 

Na POO algumas linguagens permitem a programação com Herança Múltipla. Esta característica consiste na capacidade de uma única subclasse herdar recursos de duas ou mais superclasses, por exemplo: suponha a classe rádio-relógio herdando da classe rádio e da classe relógio seus recursos (atributos e métodos).

 
 

Um dos problemas que surgem com a herança múltipla é o conflito de nomes de atributos ou métodos herdados desse tipo de herança, pois se esta subclasse precisar acessar um atributo herdado com identificador hora, de qual das superclasses este atributo será fornecido se as duas classes o possuem?

 

Em Java só é possível a herança simples, não existindo a herança múltipla – uma classe é subclasse de apenas uma única superclasse – não existindo um filho de dois pais.

 
 

Na representação acima a classe Disquete não poderá ser criada desta forma porque ela possui duas superclasses diretamente, configurando a situação de herança múltipla. As possíveis exigências para este tipo de implementação é realizada em Java de outra forma, evitando os problemas existentes com a herança múltipla. Este novo conteúdo será abordado mais adiante neste material.

 
Com intuito de apoiar o aprendizado sobre Orientação a Objeto, sugere-se assistir a videoaula para o aperfeiçoamento no conhecimento deste conteúdo.
 

Atividade de Fixação

 

No intuito de fixar a aprendizagem iniciada por meio deste módulo e verificar como seu entendimento sobre este conteúdo está, estão sendo sugeridos alguns exercícios de fixação para serem resolvidos. Clique no link de exercícios ao lado, pois será por meio dele iniciada a lista de exercícios sobre os conteúdos estudados até este momento nesta disciplina.