sábado, 6 de abril de 2019

Lista de Exercícios em Java: Polimorfismo


Compartilhe!

Lista de Exercícios em Java: Polimorfismo

Lista de Exercícios 3

Polimorfismo

(Exercícios do professor André Santanchè.)

1. Reveja a seguir o exemplo de polimorfismo e amarração dinâmica em C++. Estude este exemplo e tente escrevê-lo em Java na célula abaixo.

Algumas coisas importantes:

  • Em linguagens OO modernas como o Java os ponteiros estão sempre presentes implicitamente.
  • Em linguagens OO modernas como o Java a amarração é sempre dinâmica, portanto, ela não precisa ser declarada explicitamente, a exemplo de como é feito com o virtual em C++.
#include <iostream>

class Poligono {
    private:
       int altura;
       int largura;
    public:
       Poligono(int altura, int largura) {
           this->altura = altura;
           this->largura = largura;
       }

       int getAltura() {
           return altura;
       }

       int getLargura() {
           return largura;
       }

       virtual float getArea() {
           return 0;
       }
};

class TrianguloRetangulo : public Poligono {
    public:
       TrianguloRetangulo(int altura, int largura) : Poligono(altura, largura) {
       }
       float getArea() {
           return getAltura() * getLargura() / 2;
       }
};

class Retangulo : public Poligono {
    public:
       Retangulo(int altura, int largura) : Poligono(altura, largura) {
       }
       float getArea() {
           return getAltura() * getLargura();
       }
};

Poligono *tr = new TrianguloRetangulo(6, 10);
Poligono *rt = new Retangulo(6, 10);

std::cout << "Área do triangulo retângulo: " << tr->getArea() << std::endl;
std::cout << "Área do retângulo: " << rt->getArea() << std::endl;

Resolução em Java:

In [1]:
public class Poligono {
    
    private int altura, largura;
    
    public Poligono(int altura, int largura) {
        this.altura = altura;
        this.largura = largura;
    }

    int getAltura() {
        return altura;
    }

    int getLargura() {
        return largura;
    }

    float getArea() {
        return 0;
    }
}

public class TrianguloRetangulo extends Poligono {
    
    public TrianguloRetangulo(int altura, int largura){
        super(altura, largura);
    }
    
    float getArea() {
        return getAltura() * getLargura() / 2;
    }
}

public class Retangulo extends Poligono {
    
    public Retangulo(int altura, int largura){
        super(altura, largura);
    }
    
    float getArea() {
        return getAltura() * getLargura();
    }
}

Poligono tr = new TrianguloRetangulo(6, 10);
Poligono rt = new Retangulo(6, 10);

System.out.println("Área do triangulo retângulo: "+tr.getArea());
System.out.println("Área do retângulo: "+rt.getArea());
Área do triangulo retângulo: 30.0
Área do retângulo: 60.0

Método toString() de Object

Veja abaixo como a classe GarotoZumbi foi modificada. O método mostra() que mostrava o zumbi no console foi substituído pelo método toString() que retorna uma String com o desenho do zumbi (mas não o imprime). Com essa nova versão, você precisa receber e imprimir no console da seguinte maneira:

System.out.println(garoto.toString());

Os outros métodos também deixaram de imprimir o desenho do zumbi e agora você precisa fazê-lo explicitamente.

In [2]:
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 String toString(boolean imprimeNome) {
        String resultado = "";
        
        // cabeleira
        if (idade >= 2)
            resultado += "  *\n";
        
        // corpo com olhos
        if (estado.equalsIgnoreCase("acordado"))
            resultado += " o*o\n";
        else
            resultado += " -*-\n";
        
        // barba
        if (idade >= 3)
            resultado += "*****\n";
        
        if (imprimeNome)
            resultado += nome + "\n";
        
        return resultado;
    }
    
    public String toString() {
        return toString(true);
    }
    
    public void cresce() {
        if (idade < 3)
            idade++;
    }
    
    public void acorda() {
        estado = "acordado";
    }
    
    public void dorme() {
        estado = "dormindo";
    }
}

GarotoZumbi garoto = new GarotoZumbi(1, "acordado", "Asdrubal");
System.out.println(garoto.toString());
garoto.dorme();
System.out.println(garoto.toString());
garoto.cresce();
System.out.println(garoto.toString());
 o*o
Asdrubal

 -*-
Asdrubal

  *
 -*-
Asdrubal

O nome do método toString() não foi escolhido por acaso. Ele é um método que está defininido na classe especial Object, que é aquela classe de onde descencem implicitamente todas as classes. Quando você não sobrescreve o método toString() ele retorna uma String o endereço do objeto na memória.

O método toString() é chamado implicitamente toda vez que você pede para imprimir um objeto sem indicar o método, portanto, no exemplo a seguir:

Poligono tr = new TrianguloRetangulo(6, 10);
System.out.println(tr);
Poligono rt = new Retangulo(6, 10);
System.out.println(rt);

Ele imprime umas sequências estranhas que são a identificação única do objeto na memória.

Entretanto, se você sobrescreve o método toString(), através do polimorfismo, o novo método será chamado implicitamente quando você pede para imprimir o objeto. Por exemplo:

GarotoZumbi garoto = new GarotoZumbi(1, "acordado", "Asdrubal");
System.out.println(garoto);
garoto.dorme();
System.out.println(garoto);
garoto.cresce();
System.out.println(garoto);

Imprime o desenho do GarotoZumbi.

In [3]:
Poligono tr = new TrianguloRetangulo(6, 10);
System.out.println(tr);
Poligono rt = new Retangulo(6, 10);
System.out.println(rt);

System.out.println("===============================");

GarotoZumbi garoto = new GarotoZumbi(1, "acordado", "Asdrubal");
System.out.println(garoto);
garoto.dorme();
System.out.println(garoto);
garoto.cresce();
System.out.println(garoto);
REPL.$JShell$19$TrianguloRetangulo@13517be0
REPL.$JShell$20$Retangulo@509562ee
===============================
 o*o
Asdrubal

 -*-
Asdrubal

  *
 -*-
Asdrubal

2. Considere os herdeiros Monstro e Perfeito abaixo implementados à moda antiga com o método mostra() (eles não funcionarão agora se você tentar executá-los porque a superclasse -- GarotoZumbi -- não tem mais o mostra()).

Adapte ambos para que passem a ter o método toString() que retorna o desenho na forma de String, substituindo o mostra().

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

    public void toString(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();
|       public void toString(boolean imprimeNome) {
|           super.mostra(false);
|   
|           if (idade >= 4)
|               System.out.println("#####");
|           if (idade >= 5)
|               System.out.println("/   \\");
|   
|           if (imprimeNome)
|               System.out.println(nome);
|       }
toString(boolean) in Monstro cannot override toString(boolean) in GarotoZumbi
  return type void is not compatible with java.lang.String

|           super.mostra(false);
cannot find symbol
  symbol: method mostra(boolean)

|           mostra();
cannot find symbol
  symbol:   method mostra()
In [5]:
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
+-----+
|     |
| $-$ |
| ___ |
|     |
\_____/
Zandor
+-----+
|     |
| @-@ |
|  ~  |
|     |
\_____/
Zandor

Resolução:

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

    public String toString(boolean imprimeNome) {
    
        String resultado = "";
         
        resultado += super.toString(false);

        if (idade >= 4)
            resultado += "#####\n";
        if (idade >= 5)
            resultado += "/   \\\n";

        if (imprimeNome)
            resultado += nome+"\n";
        
        return resultado;
    }

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

Monstro monst = new Monstro(1, "acordado", "Bonerges");
System.out.println(monst);
monst.cresce();
System.out.println(monst);
monst.cresce();
System.out.println(monst);
monst.cresce();
System.out.println(monst);
monst.cresce();
System.out.println(monst);
monst.cresce();
System.out.println(monst);
monst.dorme();
System.out.println(monst);
 o*o
Bonerges

  *
 o*o
Bonerges

  *
 o*o
*****
Bonerges

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

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

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

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

In [7]:
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 String toString() {
    
        String resultado = "";
        
        resultado += "+-----+\n";

        if (idade >= 2)
            resultado += "|     |\n";

        if (estado.equals("dormindo"))
            resultado += " - -\n|  _  |\n"; 
        else if (situacao.equals("normal")) 
            resultado += "| o-o |\n| ___ |\n"; 
        else if (situacao.equals("milionario")) 
            resultado += "| $-$ |\n| ___ |\n"; 
        else if (situacao.equals("doido"))
            resultado += "| @-@ |\n|  ~  |\n";
        

        if (idade < 3)
            resultado += " \\___/\n";
        else
            resultado += "|     |\n\\_____/\n";
        

        resultado += nome+"\n";
            
        return resultado;
    }

    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];

    }
}

Perfeito perf = new Perfeito(1, "acordado", "Zandor");
System.out.println(perf);
perf.cresce();
System.out.println(perf);
perf.cresce();
System.out.println(perf);
perf.mudaSituacao();
System.out.println(perf);
perf.mudaSituacao();
System.out.println(perf);
+-----+
| o-o |
| ___ |
 \___/
Zandor

+-----+
|     |
| o-o |
| ___ |
 \___/
Zandor

+-----+
|     |
| o-o |
| ___ |
|     |
\_____/
Zandor

+-----+
|     |
| $-$ |
| ___ |
|     |
\_____/
Zandor

+-----+
|     |
| @-@ |
|  ~  |
|     |
\_____/
Zandor

3. Dada a classe Servicos,

public class Servicos {

}

crie nela um método estático chamado imprimeZumbi. Esse método deve receber como parâmetro uma referência para um objeto da classe GarotoZumbi (considere a nova versão com toString()), ou um de seus herdeiros, e deve imprimi-lo.

Utilize uma solução que explore o polimorfismo, sem o uso de condicional.

In [8]:
public class Servicos {
    
    public Servicos(){ }
    
    public static void imprimeZumbi(GarotoZumbi g){
        System.out.println(g);
    }
}

//Utilização
GarotoZumbi garoto = new GarotoZumbi(3, "acordado", "Ariel");
GarotoZumbi perf = new Perfeito(4, "acordado", "Zandor");
GarotoZumbi monst = new Monstro(5, "acordado", "Bonerges");

Servicos.imprimeZumbi(garoto);
Servicos.imprimeZumbi(perf);
Servicos.imprimeZumbi(monst);
  *
 o*o
*****
Ariel

+-----+
|     |
| o-o |
| ___ |
|     |
\_____/
Zandor

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

4. Dada as classes a seguir:

Representa o total de despesas de um mês:

In [9]:
public class DespesaMes {
    private int mes; // mes da despesa
    private float valor; // valor da despesa
     
    public DespesaMes(int mes, float valor) {
        this.mes = mes;
        this.valor = valor;
    }
    public int getMes() {
        return mes;
    }
    public float getValor() {
        return valor;
    }
}

Representa o total de despesas de um dia:

In [10]:
public class DespesaDia extends DespesaMes {
    private int dia; // dia da despesa

    public DespesaDia(int dia, int mes, float valor) {
        super(mes, valor);
        this.dia = dia;
    }

    public int getDia() {
        return dia;
    }
}

Escreva uma classe que representa todas as despesas de um indivíduo. Ela mantém um vetor onde podem ser registradas tanto despesas de um dia (DespesaDia), quanto despesas de um mês (DepesaMes). A classe implementa os seguintes métodos:

Construtor: recebe como parâmetro o CPF e um vetor com as despesas de um indivíduo e as guarda.
getCPF: retorna o CPF do indivíduo.
totalizaMes: recebe um parâmetro com um mês (int) e retorna um objeto da classe DespesaMes onde estará registrada a soma de todas as despesas que o indivíduo fez naquele mês.

In [11]:
public class DespesasIndividuo{

    private DespesaMes d[]; 
    private String cpf;

    public DespesasIndividuo(String cpf, DespesaMes[] d){
    
        this.cpf = cpf;
        this.d = d;

    }
    
    public String getCPF(){
        return cpf;
    }
    
    public DespesaMes totalizaMes(int mes){
    
        float soma = 0f;
        for(int i = 0; i<d.length; i++)
            if(d[i].getMes() == mes)
                soma += d[i].getValor();
                
        return new DespesaMes(mes, soma);
    }
}

//Utilização
DespesaMes despesas[] = new DespesaMes[7];

//Em jan.
despesas[0] = new DespesaDia(8,1,50f);
despesas[1] = new DespesaDia(9,1,40f);

//Em fev.
despesas[2] = new DespesaMes(2,5);

//Em mar.
despesas[3] = new DespesaDia(7,3,4f);
despesas[4] = new DespesaDia(8,3,4f);

//Em abr.
despesas[5] = new DespesaMes(4,5);
despesas[6] = new DespesaMes(4,8);

DespesasIndividuo dI = new DespesasIndividuo("123456", despesas);

DespesaMes despDoMes;
for(int i = 0; i<4; i++){
    despDoMes = dI.totalizaMes(i+1);
    System.out.println("O indivíduo de CPF "+dI.getCPF()+" gastou "+despDoMes.getValor()+" no mês "+despDoMes.getMes());
}
O indivíduo de CPF 123456 gastou 90.0 no mês 1
O indivíduo de CPF 123456 gastou 5.0 no mês 2
O indivíduo de CPF 123456 gastou 8.0 no mês 3
O indivíduo de CPF 123456 gastou 13.0 no mês 4

5. Considere a classe EmprestimoComposto desenvolvida anteriormente em sala de aula:

In [12]:
import java.lang.Math;

class EmprestimoComposto {
    private float s;
    private int   n;
    private float j;
    private int   corrente;
    private float p,
                  proxima;

    public EmprestimoComposto(float s, int n, float j) {
        this.s = s;
        this.n = n;
        this.j = j;
        corrente = 1;
        this.p = -1;  // antes da primeira parcela
        this.proxima = s;
    }

    int getN() {
        return n;
    }
    
    float getJ() {
        return j;
    }

    float getP() {
        return p;
    }

    public float proximaParcela() {
        p = proxima;
        corrente++;
        if (corrente <= n)
            proxima += (proxima * (j/100));
        else
            proxima = 0;
        return p;
    }
    
    public float parcela() {
        return p;
    }
    
    public float parcela(int numero) {
        float resultado = 0;
        if (numero <= n)
            resultado = s * (float)Math.pow(1 + j/100, numero-1);
        return resultado;
    }
}

// codigo principal

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

int i = 1;
emprestimo1.proximaParcela();
emprestimo2.proximaParcela();
while (emprestimo1.parcela() > 0 || emprestimo2.parcela() > 0) {
    if (emprestimo1.parcela() > 0) {
        System.out.println("Emprestimo 1: parcela " + i + " eh " + emprestimo1.parcela());
        System.out.println("              parcela " + i + " eh " + emprestimo1.parcela(i));
    }
    if (emprestimo2.parcela() > 0) {
        System.out.println("Emprestimo 2: parcela " + i + " eh " + emprestimo2.parcela());
        System.out.println("              parcela " + i + " eh " + emprestimo2.parcela(i));
    }
    emprestimo1.proximaParcela();
    emprestimo2.proximaParcela();
    i++;
}
Emprestimo 1: parcela 1 eh 200.0
              parcela 1 eh 200.0
Emprestimo 2: parcela 1 eh 500.0
              parcela 1 eh 500.0
Emprestimo 1: parcela 2 eh 202.0
              parcela 2 eh 202.0
Emprestimo 2: parcela 2 eh 510.0
              parcela 2 eh 510.0
Emprestimo 1: parcela 3 eh 204.02
              parcela 3 eh 204.02
Emprestimo 2: parcela 3 eh 520.2
              parcela 3 eh 520.19995
Emprestimo 1: parcela 4 eh 206.06021
              parcela 4 eh 206.0602
Emprestimo 2: parcela 4 eh 530.604
              parcela 4 eh 530.60394
Emprestimo 1: parcela 5 eh 208.12082
              parcela 5 eh 208.1208
Emprestimo 2: parcela 5 eh 541.21606
              parcela 5 eh 541.216
Emprestimo 2: parcela 6 eh 552.0404
              parcela 6 eh 552.04034
Emprestimo 2: parcela 7 eh 563.08124
              parcela 7 eh 563.0811

Crie uma classe Emprestimo que generalize qualquer emprestimo e EmprestimoComposto passa a ser herdeira da mesma. Transfira para a superclasse o máximo de atributos e métodos, de tal modo que possam ser reusados pelos herdeiros.

Crie uma classe EmprestimoSimples herdeira de Emprestimo que também disponha dos métodos parcela() e proximaParcela().

In [13]:
import java.lang.Math;

class Emprestimo {
    protected float s;
    protected int   n;
    protected float   j;
    protected int   corrente;
    protected float p,
                  proxima;
    
    public Emprestimo(float s, int n, float j){
        this.s = s;
        this.n = n;
        corrente = 1;
        this.j=j;
        this.p = -1;  // antes da primeira parcela
        this.proxima = s;
    }
    
    int getN() {
        return n;
    }
    
    float getJ() {
        return 0;
    }

    float getP() {
        return p;
    }
    
    public float proximaParcela() {
        return p;
    }
    
    public float parcela() {
        return p;
    }
    
    public float parcela(int numero) {
        return 0;
    }
    
    
}

class EmprestimoSimples extends Emprestimo {
    
    public EmprestimoSimples(float s, int n, float j){
        super(s, n, j);
    }
    
    public float proximaParcela() {
        p = proxima;
        corrente++;
        if (corrente <= n)
            proxima = s+s*n*(j/100);
        else
            proxima = 0;
        return p;
    }
    
    public float parcela(int numero) {
        float resultado = 0;
        if (numero <= n)
            resultado = s+s*n*(j/100);
        return resultado;
    }
    
}


class EmprestimoComposto extends Emprestimo {

    public EmprestimoComposto(float s, int n, float j) {
        super(s, n, j);
    }
    
    float getJ() {
        return j;
    }

    public float proximaParcela() {
        p = proxima;
        corrente++;
        if (corrente <= n)
            proxima += (proxima * (j/100));
        else
            proxima = 0;
        return p;
    }
    
    public float parcela(int numero) {
        float resultado = 0;
        if (numero <= n)
            resultado = s * (float)Math.pow(1 + j/100, numero-1);
        return resultado;
    }
}

6. Escreva um programa que defina um vetor de Emprestimos que pode conter tanto instâncias de EmprestimoSimples quanto EmprestimoComposto. O programa deve percorrer o vetor e imprimir a próxima parcela de cada financiamento.

In [14]:
Emprestimo vetEmprestimos[] = new Emprestimo[2];

//Instanciando no vetor os empréstimos de tipos diferentes
vetEmprestimos[0] = new EmprestimoComposto(200, 5, 1);
vetEmprestimos[1] = new EmprestimoSimples(500, 7, 2);

//Imprimindo
for(Emprestimo e: vetEmprestimos)
    do System.out.println(e.proximaParcela());
    while(e.parcela()>0)
200.0
202.0
204.02
206.06021
208.12082
0.0
500.0
570.0
570.0
570.0
570.0
570.0
570.0
0.0