Herança

A herança é um mecanismo que permite estender uma classe sem modificar a classe original. Isso tem diversos benefícios que serão estudados posteriormente. Considere a classe Emprestimo que desenvolvemos na etapa anterior. O modificador de acesso dos atributos foi alterado para protected; explicaremos isso a seguir.


In [5]:
class Emprestimo {
    protected float s;
    protected int   n;
    protected float j;
    protected int   corrente;
    protected float p;

    public Emprestimo(float s, int n, float j) {
        this.s = s;
        this.n = n;
        this.j = j;
        corrente = 1;
        this.p = s;
    }

    public float proximaParcela() {
        float retorno = p;
        corrente++;
        if (corrente <= n)
            p = p + (p * (j/100));
        else
            p = 0;
        return retorno;
    }
}

// codigo principal

Emprestimo emprestimo1 = new Emprestimo(200, 5, 1),
           emprestimo2 = new Emprestimo(500, 7, 2);

int i = 1;
float p1 = emprestimo1.proximaParcela();
float p2 = emprestimo2.proximaParcela();
while (p1 > 0 || p2 > 0) {
    if (p1 > 0)
        System.out.println("Emprestimo 1: parcela " + i + " eh " + p1);
    if (p2 > 0)
        System.out.println("Emprestimo 2: parcela " + i + " eh " + p2);
    p1 = emprestimo1.proximaParcela();
    p2 = emprestimo2.proximaParcela();
    i++;
}


Emprestimo 1: parcela 1 eh 200.0
Emprestimo 2: parcela 1 eh 500.0
Emprestimo 1: parcela 2 eh 202.0
Emprestimo 2: parcela 2 eh 510.0
Emprestimo 1: parcela 3 eh 204.02
Emprestimo 2: parcela 3 eh 520.2
Emprestimo 1: parcela 4 eh 206.06021
Emprestimo 2: parcela 4 eh 530.604
Emprestimo 1: parcela 5 eh 208.12082
Emprestimo 2: parcela 5 eh 541.21606
Emprestimo 2: parcela 6 eh 552.0404
Emprestimo 2: parcela 7 eh 563.08124

Classe herdeira

Suponha que você quer criar uma classe herdeira chamada EmprestimoPlus. Essa classe acrescenta um método totalPagar que é capaz de calcular o valor total que será pago no empréstimo.

Para criar a classe herdeira basta usar a cláusula extends:

public class EmprestimoPlus extends Emprestimo

Como os atributos estavam private, nem mesmo a classe herdeira pôde acessá-los. O modificador protected permite que os herdeiros acessem os atributos (mas não objetos externos).


In [6]:
public class EmprestimoPlus extends Emprestimo {
    public EmprestimoPlus(float s, int n, float j) {
        super(s, n, j);
    }
    
    public float totalPagar() {
        float total = 0,
              parcela = p;
        
        for (int i = 1; i < n; i++) {
            total += parcela;
            parcela = parcela + (parcela * (j/100));
        }
        
        return total;        
    }
}

EmprestimoPlus emprestimo1 = new EmprestimoPlus(200, 5, 1),
               emprestimo2 = new EmprestimoPlus(500, 7, 2);
System.out.println("Total a pagar Emprestimo 1: " + emprestimo1.totalPagar());
System.out.println("Total a pagar Emprestimo 2: " + emprestimo2.totalPagar());


Total a pagar Emprestimo 1: 812.0802
Total a pagar Emprestimo 2: 3154.0605

Evitando o protected

Alguns especialistas não gostam do uso do protected, como discutiremos adiante. Modifique as classes Emprestimo e EmprestimoPlus de modo que a classe EmprestimoPlus consiga acessa os atributos de Emprestimo sem o uso de public nem protected neles.


In [8]:
class Emprestimo {
    private float s;
    private int   n;
    private float j;
    private int   corrente;
    private float p;

    public Emprestimo(float s, int n, float j) {
        this.s = s;
        this.n = n;
        this.j = j;
        corrente = 1;
        this.p = s;
    }
    
    int getN() {
        return n;
    }
    
    float getJ() {
        return j;
    }

    float getP() {
        return p;
    }

    public float proximaParcela() {
        float retorno = p;
        corrente++;
        if (corrente <= n)
            p = p + (p * (j/100));
        else
            p = 0;
        return retorno;
    }
}

public class EmprestimoPlus extends Emprestimo {
    public EmprestimoPlus(float s, int n, float j) {
        super(s, n, j);
    }
    
    public float totalPagar() {
        float total = 0,
              parcela = getP();
        
        for (int i = 1; i < getN(); i++) {
            total += parcela;
            parcela = parcela + (parcela * (getJ()/100));
        }
        
        return total;        
    }
}

EmprestimoPlus emprestimo1 = new EmprestimoPlus(200, 5, 1),
               emprestimo2 = new EmprestimoPlus(500, 7, 2);
System.out.println("Total a pagar Emprestimo 1: " + emprestimo1.totalPagar());
System.out.println("Total a pagar Emprestimo 2: " + emprestimo2.totalPagar());


Total a pagar Emprestimo 1: 812.0802
Total a pagar Emprestimo 2: 3154.0605

Herança com Garoto Zumbi

Crie duas classes herdeiras de GarotoZumbi com as seguintes funcionalidades:

  • Monstro - consegue crescer mais que o GarotoZumbi (até cinco anos de idade):
o*o

  *
 o*o

  *
 o*o
*****

  *
 o*o
*****
#####

  *
 o*o
*****
#####
/   \
  • Perfeito - tem outra aparência e tem dois outros estados milionario e doido; acrescenta um método chamado mudaEstado() que muda o estado (ciclicamente) entre normal, milionario e doido.
+-----+
| o-o |
| ___ |
 \___/
1 ano
+-----+
|     |
| o-o |
| ___ |
 \___/
2 anos
+-----+
|     |
| o-o |
| ___ |
|     |
\_____/
3 anos (limite)
+-----+
|     |
| $-$ |
| ___ |
|     |
\_____/
milionário
+-----+
|     |
| @-@ |
|  ~  |
|     |
\_____/
doido

In [17]:
public class GarotoZumbi
{
    protected int idade;
    protected String estado;
    protected String nome;
    
    public GarotoZumbi(int idade, String estado, String nome) {
        this.idade = idade;
        this.estado = estado;
        this.nome = nome;
    }
    
    public void mostra(boolean imprimeNome) {
        // cabeleira
        if (idade >= 2)
            System.out.println("  *");
        
        // corpo com olhos
        if (estado.equalsIgnoreCase("acordado"))
            System.out.println(" o*o");
        else
            System.out.println(" -*-");
        
        // barba
        if (idade >= 3)
            System.out.println("*****");
        
        if (imprimeNome)
            System.out.println(nome);
        
        System.out.println();
    }
    
    public void mostra() {
        mostra(true);
    }
    
    public void cresce() {
        if (idade < 3)
            idade++;
        mostra();
    }
    
    public void acorda() {
        estado = "acordado";
        mostra();
    }
    
    public void dorme() {
        estado = "dormindo";
        mostra();
    }
}

GarotoZumbi garoto = new GarotoZumbi(1, "acordado", "Asdrubal");
garoto.mostra();
garoto.dorme();
garoto.cresce();


 o*o
Asdrubal

 -*-
Asdrubal

  *
 -*-
Asdrubal

Resolução

Na implementação do Monstro, os métodos mostra e cresce sobrescrevem os métodos de mesmo nome da superclasse.

No caso do método mostra, ele estende o da superclasse e, portanto, aciona o método da superclasse e acrescenta sua extensão. O método cresce só sobrescreve o da superclasse.


In [19]:
public class Monstro extends GarotoZumbi
{
    public Monstro(int idade, String estado, String nome) {
        super(idade, estado, nome);
    }

    public void mostra(boolean imprimeNome) {
        super.mostra(false);

        if (idade >= 4)
            System.out.println("#####");
        if (idade >= 5)
            System.out.println("/   \\");

        if (imprimeNome)
            System.out.println(nome);
    }

    public void cresce() {
        if (idade < 5)
            idade++;
        mostra();
    }
}

Monstro monst = new Monstro(1, "acordado", "Bonerges");
monst.mostra();
monst.cresce();
monst.cresce();
monst.cresce();
monst.cresce();
monst.cresce();
monst.dorme();


 o*o

Bonerges
  *
 o*o

Bonerges
  *
 o*o
*****

Bonerges
  *
 o*o
*****

#####
Bonerges
  *
 o*o
*****

#####
/   \
Bonerges
  *
 o*o
*****

#####
/   \
Bonerges
  *
 -*-
*****

#####
/   \
Bonerges

In [20]:
public class Perfeito extends GarotoZumbi
{
    private String situacao = "normal";
    
    public Perfeito(int idade, String estado, String nome) {
        super(idade, estado, nome);
    }

    public Perfeito(int idade, String estado, String nome, String situacao) {
        super(idade, estado, nome);
        this.situacao = situacao;
    }

    public void mostra() {
        System.out.println("+-----+");

        if (idade >= 2)
            System.out.println("|     |");

        if (estado.equals("dormindo")) {
            System.out.println(" - -");
            System.out.println("|  _  |"); }
        else if (situacao.equals("normal")) {
            System.out.println("| o-o |");
            System.out.println("| ___ |"); }
        else if (situacao.equals("milionario")) {
            System.out.println("| $-$ |");
            System.out.println("| ___ |"); }
        else if (situacao.equals("doido")) {
            System.out.println("| @-@ |");
            System.out.println("|  ~  |");
        }

        if (idade < 3)
            System.out.println(" \\___/");
        else {
            System.out.println("|     |");
            System.out.println("\\_____/");
        }

            System.out.println(nome);
    }

    public void mudaSituacao() {
        String s[] = {"normal",
                      "milionario",
                      "doido" };

        int p;
        for (p = 0; p < s.length && !situacao.equals(s[p]); p++)
            /* nada */ ;

        p = (p + 1) % 3;
        situacao = s[p];

        mostra();
    }
}

Perfeito perf = new Perfeito(1, "acordado", "Zandor");
perf.mostra();
perf.cresce();
perf.cresce();
perf.mudaSituacao();
perf.mudaSituacao();


+-----+
| o-o |
| ___ |
 \___/
Zandor
+-----+
|     |
| o-o |
| ___ |
 \___/
Zandor
+-----+
|     |
| o-o |
| ___ |
|     |
\_____/
Zandor
+-----+
|     |
| $-$ |
| ___ |
|     |
\_____/
Zandor
+-----+
|     |
| @-@ |
|  ~  |
|     |
\_____/
Zandor