Polimorfismo

Padrão

Olá pessoal! Hoje encerramos a nossa série sobre orientação a objetos… sim, como eu disse lá no primeiro post, esta série é bem mais curta que a nossa primeira, sobre Programação para Iniciantes. Mas acredito que ao longo destes 5 posts, foi possível pelo menos dar uma noção sobre orientação a objetos. Obviamente, não tratei de conceitos complexos (afinal eu nem sou autoridade no assunto pra abordar tópicos muito avançados).

Então, sem mais delongas, vamos falar sobre o tal do Polimorfismo. Sim… um nome “feio”, mas nem por isso é complicado. Praticamente, todo o conceito que envolve o polimorfismo já foi visto no último post, sobre métodos e classes abstratas.

Mas o que vem a ser o polimorfismo (polymorphism, em inglês) é uma técnica que permite que referências de tipos de classes mais abstratas representem o comportamento das classes concretas que referenciam. Dessa forma, é possível tratar vários tipos de maneira homogênea (através da interface do tipo mais abstrato).

Resumidamente, podemos dizer que o polimorfismo busca obter um comportamento específico através de uma mesma interface da classe. Neste post, veremos duas formas de utilizar o polimorfismo.

A primeira forma que veremos, é a utilização de uma superclasse abstrata, que terá seus métodos implementados pelas subclasses. A partir disso, acessaremos os métodos implementados pelas subclasses através da interface da superclasse. Parece confuso, mas deve ficar mais claro no código.

Como sempre, primeiro em C++…

#include <iostream>
#include <string>
#include <vector>
using namespace std;

class Animal {
public:
	string nome;

	Animal(string nome) {
		this->nome = nome;
	}

	virtual string falar() = 0;

	virtual ~Animal() {
	}
};

class Gato: public Animal {
public:
	Gato(string nome): Animal(nome) {
	}

	string falar() {
		return "Meow!";
	}
};

class Cachorro: public Animal {
public:
	Cachorro(string nome): Animal(nome) {
	}

	string falar() {
		return "Woof! Woof!";
	}
};

int main(void) {

	vector animais;
	animais.push_back(new Gato("Beto"));
	animais.push_back(new Cachorro("Rufus"));

	for (int i = 0; i < animais.size(); i++) {
		cout << animais[i]->nome << " diz: " << animais[i]->falar() << endl;
	}

	return 0;
}

…e depois em Java…

import java.util.*;

abstract class Animal {

    public String nome;

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

    public abstract String falar();
}

class Gato extends Animal {

    public Gato(String nome) {
        super(nome);
    }

    @Override
    public String falar() {
        return "Meow!";
    }
}

class Cachorro extends Animal {

    public Cachorro(String nome) {
        super(nome);
    }

    @Override
    public String falar() {
        return "Woof! Woof!";
    }
}

public class ExemploPolimorfismo {

    public static void main(String[] args) {
        Collection animais = new ArrayList();
        animais.add(new Gato("Beto"));
        animais.add(new Cachorro("Rufus"));

        for (Animal a : animais) {
            System.out.println(a.nome + " diz: " + a.falar());
        }
    }
}

Como podemos ver em nosso exemplo, criamos uma classe abstrata chamada Animal, que possui as implementações Gato e Cachorro. Acredito que a única “novidade” neste código é o fato de chamarmos o construtor da superclasse em cada uma das implementações. Percebam que, em C++, devemos colocar : em frente ao construtor da subclasse e fazermos a chamada a ela. Já no Java, a superclasse é sempre referenciada pela palavra-chave super, de forma que chamamos seu construtor chamando super() e passando seus parâmetros.

Na parte executável do programa, nós criamos uma lista (em C++ um vector – nada mais que um vetor dinâmico, e em Java um ArrayList) de objetos, declarando como tipo a classe Animal, mais genérica. Dessa forma, apesar de ser um tipo abstrato, os objetos que compõem a lista são as implementações que fizemos (no caso, Gato e Cachorro). Com isso, temos uma interface padrão pra acesso aos objetos, independente de seu tipo, já que definimos os objetos pela interface Animal, e não pelas implementações específicas. Isso é polimorfismo: acessando uma mesma interface, podemos obter comportamentos diferentes. Assim, se adicionarmos novas implementações da classe Animal, elas também responderão a esse comportamento específico.

Outra forma de utilizarmos o polimorfismo é através de sobrecarga de funções / métodos. Dessa forma, uma determinada função pode ter um comportamento específico de acordo com seus parâmetros. Vamos ver um exemplo em C++…

#include <iostream>
using namespace std;

class Tipo {
public:
	void verTipo(int t) {
		cout << "Inteiro" << endl;
	}

	void verTipo(float t) {
		cout << "Float" << endl;
	}

	void verTipo(double t) {
		cout << "Double" << endl;
	}

	void verTipo(long t) {
        cout << "Long" << endl;
	}
};

int main(void) {

	Tipo t;
	t.verTipo(1);
	t.verTipo(1.0F);
	t.verTipo(1.0);
	t.verTipo(1l);

	return 0;
}

…e em Java…

class Tipo {

	public void verTipo(int t) {
		System.out.println("Inteiro");
	}

	public void verTipo(float t) {
		System.out.println("Float");
	}

	public void verTipo(double t) {
		System.out.println("Double");
	}

	public void verTipo(long t) {
		System.out.println("Long");
	}
}

public class ExemploPolimorfismo {

	public static void main(String[] args) {
		Tipo t = new Tipo();
		t.verTipo(1);
		t.verTipo(1.0F);
		t.verTipo(1.0);
		t.verTipo(1L);
	}
}

Neste exemplo, criamos um (ou melhor, alguns) método chamado vetTipo que se comporta de acordo com o tipo de dado que ele recebe. Dessa forma, este método tem um comportamento polimórfico, ajustando o seu comportamento de acordo com o dado recebido. Bacana, não?

Bom pessoal, é isso. Espero que tenham gostado da série sobre orientação a objetos, e que tenha servido pra ajudar a compreender esses conceitos tão básicos. A partir dele, poderemos trabalhar com coisas mais elaboradas aqui no blog! Aguardem, pois vem bastante coisa legal por aí…

Abraço e até o próximo post! 😀

Um comentário sobre “Polimorfismo

Deixe uma resposta