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 &lt;iostream&gt;<br />
#include &lt;string&gt;<br />
#include &lt;vector&gt;<br />
using namespace std;</p>
<p>class Animal {<br />
public:<br />
	string nome;</p>
<p>	Animal(string nome) {<br />
		this-&gt;nome = nome;<br />
	}</p>
<p>	virtual string falar() = 0;</p>
<p>	virtual ~Animal() {<br />
	}<br />
};</p>
<p>class Gato: public Animal {<br />
public:<br />
	Gato(string nome): Animal(nome) {<br />
	}</p>
<p>	string falar() {<br />
		return &quot;Meow!&quot;;<br />
	}<br />
};</p>
<p>class Cachorro: public Animal {<br />
public:<br />
	Cachorro(string nome): Animal(nome) {<br />
	}</p>
<p>	string falar() {<br />
		return &quot;Woof! Woof!&quot;;<br />
	}<br />
};</p>
<p>int main(void) {</p>
<p>	vector animais;<br />
	animais.push_back(new Gato(&quot;Beto&quot;));<br />
	animais.push_back(new Cachorro(&quot;Rufus&quot;));</p>
<p>	for (int i = 0; i &lt; animais.size(); i++) {<br />
		cout &lt;&lt; animais[i]-&gt;nome &lt;&lt; &quot; diz: &quot; &lt;&lt; animais[i]-&gt;falar() &lt;&lt; endl;<br />
	}</p>
<p>	return 0;<br />
}

…e depois em Java…

<br />
import java.util.*;</p>
<p>abstract class Animal {</p>
<p>    public String nome;</p>
<p>    public Animal(String nome) {<br />
        this.nome = nome;<br />
    }</p>
<p>    public abstract String falar();<br />
}</p>
<p>class Gato extends Animal {</p>
<p>    public Gato(String nome) {<br />
        super(nome);<br />
    }</p>
<p>    @Override<br />
    public String falar() {<br />
        return &quot;Meow!&quot;;<br />
    }<br />
}</p>
<p>class Cachorro extends Animal {</p>
<p>    public Cachorro(String nome) {<br />
        super(nome);<br />
    }</p>
<p>    @Override<br />
    public String falar() {<br />
        return &quot;Woof! Woof!&quot;;<br />
    }<br />
}</p>
<p>public class ExemploPolimorfismo {</p>
<p>    public static void main(String[] args) {<br />
        Collection animais = new ArrayList();<br />
        animais.add(new Gato(&quot;Beto&quot;));<br />
        animais.add(new Cachorro(&quot;Rufus&quot;));</p>
<p>        for (Animal a : animais) {<br />
            System.out.println(a.nome + &quot; diz: &quot; + a.falar());<br />
        }<br />
    }<br />
}

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 &lt;iostream&gt;<br />
using namespace std;</p>
<p>class Tipo {<br />
public:<br />
	void verTipo(int t) {<br />
		cout &lt;&lt; &quot;Inteiro&quot; &lt;&lt; endl;<br />
	}</p>
<p>	void verTipo(float t) {<br />
		cout &lt;&lt; &quot;Float&quot; &lt;&lt; endl;<br />
	}</p>
<p>	void verTipo(double t) {<br />
		cout &lt;&lt; &quot;Double&quot; &lt;&lt; endl;<br />
	}</p>
<p>	void verTipo(long t) {<br />
        cout &lt;&lt; &quot;Long&quot; &lt;&lt; endl;<br />
	}<br />
};</p>
<p>int main(void) {</p>
<p>	Tipo t;<br />
	t.verTipo(1);<br />
	t.verTipo(1.0F);<br />
	t.verTipo(1.0);<br />
	t.verTipo(1l);</p>
<p>	return 0;<br />
}

…e em Java…

class Tipo {</p>
<p>	public void verTipo(int t) {<br />
		System.out.println(&quot;Inteiro&quot;);<br />
	}</p>
<p>	public void verTipo(float t) {<br />
		System.out.println(&quot;Float&quot;);<br />
	}</p>
<p>	public void verTipo(double t) {<br />
		System.out.println(&quot;Double&quot;);<br />
	}</p>
<p>	public void verTipo(long t) {<br />
		System.out.println(&quot;Long&quot;);<br />
	}<br />
}</p>
<p>public class ExemploPolimorfismo {</p>
<p>	public static void main(String[] args) {<br />
		Tipo t = new Tipo();<br />
		t.verTipo(1);<br />
		t.verTipo(1.0F);<br />
		t.verTipo(1.0);<br />
		t.verTipo(1L);<br />
	}<br />
}

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! 😀