Introdução ao Polimorfismo #
O assunto em si, não é muito claro para vários programadores, e algumas pessoas explicam de forma equivocada, mais adiante vai perceber o porquê. A princípio, polimorfismo quer dizer ‘várias formas’, pode-se dizer que é o ato do mesmo código suportar diversas coisas.
O foco aqui não é a implementação e sim no conceito, por tanto irei alternar no uso da linguagem para exemplos.
1. Subtyping #
Esse é o polimorfismo que pode ser considerado entre os mais conhecidos, pois é o que muitos costumam dizer quando falam de ‘polimorfismo’. São subclasses que fornecem diferentes implementações de algum método da super classe.
public class Animal {
public void eat() {
System.out.println( "Animal eating" );
}
}
public class Chicken extends Animal {
public void eat() {
System.out.println( "Chicken eating" );
}
}
public class Cat extends Animal {
public void eat() {
System.out.println( "Cat eating" );
}
}
public class Test {
public void makeAnimalEat( Animal animal ) {
animal.eat();
}
public static void main( String[] args ) {
Test t = new Test();
t.makeAnimalEat(new Animal());
t.makeAnimalEat(new Chicken());
t.makeAnimalEat(new Cat());
}
}
2. Ad-hoc Polymorphism (Overloading) #
Também conhecido como sobrecarga, você basicamente terá a execução do método de acordo com quantidade ou tipos de parâmetros da função, então dependerá do que for passado. Por exemplo:
class Person
def show_data(name)
"Hey meu nome é #{name}"
end
def show_data(name,age)
"Hey meu nome é #{name} e tenho #{age} anos"
end
end
person1 = Person.new
puts person1.show_data("Victor Igor")
puts person1.show_data("Victor Igor", 18)
3. Ad-hoc Polymorphism (Coercion polymorphism) #
Acontece quando um tipo primitivo ou um objeto é ‘convertido’ em outro tipo de objeto ou tipo primitivo, e essas conversões podem ser implícitas(são feitas automaticamente) ou explicitada.
Coerções são essencialmente uma forma de abreviatura que pode reduzir o tamanho do programa e legibilidade do programa, mas também pode causar erros de sistema sutis e, por vezes, perigosos. Coerções é geralmente detectada no tempo de compilação, mas em linguagens impuras como LISP, têm abundância de coerções que são apenas detectado e executado em tempo de execução.
Um exemplo de Coerção bem comum é de um número ponto flutuante que pode ser usado aonde se espera um inteiro.
float b = 2;
int a = 3.33;
a = (int) 10.2;
4. Parametric Polymorphism #
Também é chamado de Generics em Java e C#, e templates em C++. Diferente de Ad-hoc(overloading), basicamente é uma função ou um tipo de dado que pode ser escrito genericamente para que ele possa lidar com valores de forma idêntica sem depender do seu tipo. Generics pode ser feito tanto com estruturas ou classes, por exemplo:
Estrutura:
public class GenericMethodTest
{
public static < E > void printArray( E[] inputArray )
{
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
public static void main( String args[] )
{
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println( "Array integerArray:" );
printArray( intArray );
System.out.println( "\nArray doubleArray:" );
printArray( doubleArray );
System.out.println( "\nArray characterArray:" );
printArray( charArray );
}
}
Esse tipo de polimorfismo só ocorrem em linguagens tipadas, e não é tão comum em linguagens OO como Java e C#, e sim mais comum em linguagens funcionais como Haskell e OCaml.
5. Structural Polymorphism #
Imagine uma função que executa em uma tal estrutura de um valor. Porém ela funciona para todos os valores que são super conjuntos dessa estrutura. Calma, você vai entender. Sei que pode parecer um pouco confuso sobre essa ‘estrutura de um valor’, vamos ver um exemplo:
class Airplane{
constructor(e::<Left>, s::<Top>, d::<Right>){
this.left<- e
this.top<- s
this.right<- d
}
}
interface Direction{
left: <Left>
right: <Right>
}
move(direc: Direcao<Int, Int>){
return direc.left- direc.right
}
move(new Airplane(5, 6, 2)) ;;=> 3
move(new Direction(3, 1)) ;;=> 3
A maioria das linguagens tipadas por serem de tipos nominais, elas não suportam esse tipo de polimorfismo, mas você pode fazer o uso de interfaces.
6. Row Polymorphism #
Se você entender todos que já foram citados, esse você tira de letra! :) Imagine agora usar todo o poder do Structural Polymorphism com o Parametric Polymorphism! Intependente do tipo que a função mover receber, ela fará.
class Airplane{
constructor(e::<Left>, s::<Top>, d::<Right>){
this.left<- e
this.top<- s
this.right<- d
}
}
interface Direction{
left: <Left>
right: <Right>
}
mover(direc: Direction<X, Y>){
return direc.left - direc.right
}
mover(new Airplane(5, 6, 2)) ;;=> 3
mover(new Direction(3.5, 0.5)) ;;=> 3
Conclusão #
É bem importante deixar claro que o ‘tipo’ que citei, é melhor interpretá-lo como “Algo que se parece com as propriedades que sua operação está esperando”, senão estaremos excluindo a possibilidade de polimorfismo nas linguagens com tipagem dinâmica.
Os nomes dos tipos que citei também podem ter nomes diferente e são até categorizados, pode-se dizer que até um ou outro é da subcategoria de outro.
Existe muitos tipos de polimorfismo, alguns mais acadêmicos como Polytypism que já é um tipo especial de polimorfismo e que só tem em linguagens com Generalised Algebraic Datatypes (GADTs), e o F-bounded polymorphism, e nesse momento deve ter alguém inventando mais outro, então cuidado ao querer explicar polimorfismo para alguém, pois polimorfismo não tem nada a ver com OO-class-based, e sim com o conceito original de OO que é a troca de mensagens, pois é apenas a forma da mensagem ser recebida por objetos diferentes. Cabe agora você saber como a linguagem que você desenvolve implementa polimorfismo.
🙏🙏🙏
Já que você chegou até aqui, seria muito show compartilhar este artigo em sua rede social favorita 💖! Para feedback, comente ou interaja com emoji 👻
Published