Polimorfismo
Polimorfismo é um assunto muito simples, mas de enorme utilidade para a POO. Seu nome já é bastante intuitivo, significando “muitas formas”. Basicamente o polimorfismo é a habilidade de uma entidade receber um objeto gerado a partir de uma subclasse e tratá-lo de forma genérica, como se fosse um objeto da superclasse.
O interessante é que como os objetos recebidos são diferentes, mas sempre do mesmo tipo da superclasse, isso faz com que ele reaja de maneira diferente de acordo com o objeto que está recebendo.
É mais fácil do que parece! Demonstrarei isso funcionando com um exemplo bem simples. Criarei uma estrutura de classes, conforme a figura abaixo:
Fiz um esquema bastante simples, onde nenhum atributo está presente nas classes, apenas um método.
O importante a ser observado já nesse esquema é que estamos lidando com 3 classes herdeiras e uma superclasse. Tanto Cachorro quanto Gato quanto Galinha são animais, ou seja, são classes do tipo Animal. É importante que isso seja lembrado o tempo todo, pois o polimorfismo começa a partir dessa afirmação, conforme explicarei abaixo.
Criarei uma classe chamada "Animal", que será a nossa superclasse. Seu código ficará da seguinte forma:
Fazendo uma revisão dos conceitos de classes e métodos abstratos, a classe acima, por ser abstrata, não permite instanciação, ou seja, não será possível criar um objeto do tipo Animal, mas sim gerar classes herdeiras dela. Também seremos obrigados a criar, em cada classe herdeira, um método chamado emiteSom(), já que temos um método abstrato nessa superclasse e, todos os métodos abstratos devem ter, obrigatoriamente, implementação nas subclasses.
Bem, terminada essa revisão de conceitos, vamos continuar! Criarei outras 3 classes, que serão "Cachorro", "Gato" e "Galinha", todas herdeiras da classe "Animal".
Classe Cachorro:
Classe Gato:
Classe Galinha:
Percebam que a implementação das 3 classes só difere no que será impresso pelo método.
Criei também uma simples interface com 3 radiobuttons e um botão, como mostra a imagem abaixo:
A ideia é que, a partir do que estiver selecionado no radiobutton, o sistema, ao receber o clique no botão “Emitir Som”, instancie o objeto adequado e emita o som (imprima na tela) correto referente ao animal selecionado.
Vou executar e clicar em cada um dos botões para ilustrar o resultado. Primeiro com a opção Cachorro selecionada, teremos:
Depois com a opção Gato selecionada:
E por último com a opção Galinha selecionada:
Como vocês puderam perceber, a partir da seleção no conjunto de radiobuttons, o texto adequado era exibido. Bem, a mágica do polimorfismo começará a aparecer quando eu disser que isso aconteceu chamando sempre um mesmo método, que é o emiteSom(), presente na superclasse Animal e exibido no diagrama de classes ao lado. Mas, se um mesmo método estava sendo chamado, como foi possível alterar o texto exibido? Vejam como é simples e interessante: eu disse, no início do tópico, que era importante lembrar que tanto Cachorro quanto Gato quanto Galinha eram, também, classes do tipo Animal. E, se todo Cachorro, Gato ou Galinha são do tipo Animal, podemos criar associações como essas abaixo:
Então, partindo desse princípio, o código que criei para a execução da pequena aplicação acima foi:
De acordo com o radiobutton selecionado uma subclasse era instanciada e associada ao objeto do tipo Animal. A essência do polimorfismo está nos trechos destacados pelos retângulos coloridos. Objetos do tipo Animal, que chamei de "objAnimal", recebem objetos do tipo Cachorro, Gato e Galinha sem o menor problema. Na segunda linha de cada bloco, o método emiteSom() é chamado, mas em cada uma delas trata-se de um método com implementação diferente, ou seja, são comportamentos diferentes para um mesmo método e, qual comportamento será executado é decidido dinamicamente, durante a execução do programa. Isso é polimorfismo! A lógica por trás disso é muito, muito simples! Vejam:
Todo Cachorro é um Animal, assim como todo Gato e toda Galinha. Por essa razão a atribuição abaixo é possível:
Animal objAnimal = new Cachorro();
A imagem abaixo resume muito bem o que é esse importante pilar da POO:
Caso você ainda tenha dúvidas sobre esse importante conceito, eu tenho outro exemplo. Vamos criar um vetor de objetos do tipo Animal, cujo código será:
Animal[] vetorAnimais = new Animal[3];
vetorAnimais[0] = new Cachorro();
vetorAnimais[1] = new Gato();
vetorAnimais[2] = new Galinha();
Comentando o código...
A inserção dos objetos das subclasses dentro do vetor é um exemplo de polimorfismo, pois ele, apesar de ter sido criado para receber objetos do tipo Animal, está armazenando objetos de diversas outras formas. E a prova de que a aceitação dos diferentes objetos acontece sem qualquer problema pelo vetor é que podemos criar uma estrutura de repetição para exibi-los, dessa forma:
E executando essa estrutura de repetição, teremos:
Ou seja, o vetor conseguiu armazenar corretamente os objetos das subclasses!
Isso é o polimorfismo. Espero que tenha ficado claro, pois é um dos conceitos que, apesar de não ser complicado, gera muitas dúvidas durante o aprendizado.