Polimorfismo
O termo Polimorfismo significa "ocorrendo em várias formas diferentes". No contexto da programação orientada a objetos, Polimorfismo refere-se à capacidade de uma classe fornecer uma interface comum para suas classes derivadas, permitindo que cada classe derivada implemente comportamentos específicos para os métodos herdados.
Normalmente, ao criar um objeto de uma classe, cria-se uma variável para armazenar a referência desse objeto. E o tipo da variável geralmente é a própria classe desse objeto.
Por exemplo:
index.cs
123Dog myVar = new Dog(); // or even in case of implicit declaration, 'myVar' will be of type 'Dog' var myVar = new Dog();
No entanto, também é possível criar uma variável de uma classe base e armazenar nela um objeto de uma classe derivada.
Por exemplo:
index.cs
1234567891011121314151617181920212223242526272829303132using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Console.WriteLine(example.breed); // Error because it's not accessible (read explanation below) } }
O atributo breed
do objeto Dog não está acessível porque, ao armazenar o objeto Dog
em uma variável do tipo Mammal
, ele é implicitamente convertido em Mammal
. Isso significa que apenas os atributos e métodos presentes na classe Mammal
podem ser acessados. Isso não significa que o valor armazenado na classe breed
foi perdido, apenas está inacessível e pode-se dizer que está essencialmente oculto.
É possível converter example
em um Dog
por meio de typecasting explícito e o atributo breed
ficará disponível novamente:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } public void speak() { // Empty for now } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Dog casted = (Dog)example; Console.WriteLine(casted.breed); } }
No entanto, para métodos existe um recurso adicional chamado sobrescrita de método (method overriding), que é um pouco semelhante à sobrecarga de método (method overloading). Neste caso, basicamente você redefine um método da classe base na classe derivada.
Por exemplo, pode-se desejar que Mammal
tenha um método speak()
de forma que ele exiba Woof!
se for chamado a partir de um objeto Dog
, e Meow!
se for chamado a partir de um objeto Cat
:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647using System; class Mammal { public void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // The following execute the overriden version of the methods Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); // All of these execute the 'Mammal' version of the method m1.speak(); m2.speak(); m3.speak(); } }
Os avisos exibidos durante a compilação ocorrem devido à sobreposição de nomes de métodos nas classes pai e filha. Neste caso, há sobreposição entre os métodos speak
das classes Cat
e Mammal
, e também entre as classes Dog
e Mammal
, por isso são exibidos dois avisos.
No entanto, observe que ao final m1
, m2
e m3
executam o método definido na classe Mammal
, pois eles são convertidos para objetos do tipo Mammal
.
Se desejar que o método speak
se comporte conforme a implementação do objeto original, basta convertê-lo em um método virtual. Um método virtual é simplesmente um método que permite ser sobrescrito por classes derivadas, de modo que a implementação específica executada é determinada em tempo de execução com base no tipo do objeto original.
Para tornar um método virtual, basta adicionar a palavra-chave virtual
antes do seu tipo de retorno:
index.cs
1234567class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } }
Para criar uma implementação sobrescrita deste método, utilize a palavra-chave override
antes do tipo de retorno na implementação da nova versão na classe derivada:
index.cs
123456789101112131415class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } }
Vamos agora juntar os dois trechos de código e observar como isso afeta a saída:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647using System; class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); m1.speak(); // Since it stores a Mammal() object, it executes the Mammal's implementation m2.speak(); // Output: Meow! m3.speak(); // Output: Woof! // The following behave the same as they did in the previous example: Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); } }
Observe que na saída, m2
e m3
utilizam a implementação do método das classes Cat
e Dog
, pois esses objetos foram originalmente criados como Cat
e Dog
.
Esse processo de sobrescrever métodos para que se comportem de acordo com seu tipo original é chamado de polimorfismo.
Uma situação em que isso pode ser muito útil é a possibilidade de armazenar objetos de diferentes tipos de dados em um único array ou lista.
Por exemplo:
index.cs
123456789List<Shape> shapes = new List<Shape>(); shapes.Add(new Rectangle(25, 50)); shapes.Add(new Square(50)); shapes.Add(new Circle(10)); shapes[0].getArea(); // Output: 1250.0f shapes[1].getArea(); // Output: 2500.0f shapes[2].getArea(); // Output: 314.15f
1. Quais são as duas palavras-chave usadas na sobrescrita de métodos?
2. Qual é a forma correta de tornar um método sobrescrevível?
Obrigado pelo seu feedback!
Pergunte à IA
Pergunte à IA
Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo
Can you explain more about how polymorphism works in arrays or lists?
What are some real-world examples where polymorphism is useful?
Can you show how to implement polymorphism in another programming language?
Awesome!
Completion rate improved to 2.04
Polimorfismo
Deslize para mostrar o menu
O termo Polimorfismo significa "ocorrendo em várias formas diferentes". No contexto da programação orientada a objetos, Polimorfismo refere-se à capacidade de uma classe fornecer uma interface comum para suas classes derivadas, permitindo que cada classe derivada implemente comportamentos específicos para os métodos herdados.
Normalmente, ao criar um objeto de uma classe, cria-se uma variável para armazenar a referência desse objeto. E o tipo da variável geralmente é a própria classe desse objeto.
Por exemplo:
index.cs
123Dog myVar = new Dog(); // or even in case of implicit declaration, 'myVar' will be of type 'Dog' var myVar = new Dog();
No entanto, também é possível criar uma variável de uma classe base e armazenar nela um objeto de uma classe derivada.
Por exemplo:
index.cs
1234567891011121314151617181920212223242526272829303132using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Console.WriteLine(example.breed); // Error because it's not accessible (read explanation below) } }
O atributo breed
do objeto Dog não está acessível porque, ao armazenar o objeto Dog
em uma variável do tipo Mammal
, ele é implicitamente convertido em Mammal
. Isso significa que apenas os atributos e métodos presentes na classe Mammal
podem ser acessados. Isso não significa que o valor armazenado na classe breed
foi perdido, apenas está inacessível e pode-se dizer que está essencialmente oculto.
É possível converter example
em um Dog
por meio de typecasting explícito e o atributo breed
ficará disponível novamente:
index.cs
12345678910111213141516171819202122232425262728293031323334353637383940using System; class Mammal { public int age; public Mammal(int age) { this.age = age; } public void speak() { // Empty for now } } class Dog : Mammal { public string breed; public Dog(int age, string breed) : base(age) { this.breed = breed; } } class ConsoleApp { static void Main() { Mammal example = new Dog(10, "Labrador"); Console.WriteLine(example.age); Dog casted = (Dog)example; Console.WriteLine(casted.breed); } }
No entanto, para métodos existe um recurso adicional chamado sobrescrita de método (method overriding), que é um pouco semelhante à sobrecarga de método (method overloading). Neste caso, basicamente você redefine um método da classe base na classe derivada.
Por exemplo, pode-se desejar que Mammal
tenha um método speak()
de forma que ele exiba Woof!
se for chamado a partir de um objeto Dog
, e Meow!
se for chamado a partir de um objeto Cat
:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647using System; class Mammal { public void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { // The following execute the overriden version of the methods Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); // All of these execute the 'Mammal' version of the method m1.speak(); m2.speak(); m3.speak(); } }
Os avisos exibidos durante a compilação ocorrem devido à sobreposição de nomes de métodos nas classes pai e filha. Neste caso, há sobreposição entre os métodos speak
das classes Cat
e Mammal
, e também entre as classes Dog
e Mammal
, por isso são exibidos dois avisos.
No entanto, observe que ao final m1
, m2
e m3
executam o método definido na classe Mammal
, pois eles são convertidos para objetos do tipo Mammal
.
Se desejar que o método speak
se comporte conforme a implementação do objeto original, basta convertê-lo em um método virtual. Um método virtual é simplesmente um método que permite ser sobrescrito por classes derivadas, de modo que a implementação específica executada é determinada em tempo de execução com base no tipo do objeto original.
Para tornar um método virtual, basta adicionar a palavra-chave virtual
antes do seu tipo de retorno:
index.cs
1234567class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } }
Para criar uma implementação sobrescrita deste método, utilize a palavra-chave override
antes do tipo de retorno na implementação da nova versão na classe derivada:
index.cs
123456789101112131415class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } }
Vamos agora juntar os dois trechos de código e observar como isso afeta a saída:
index.cs
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647using System; class Mammal { public virtual void speak() { Console.WriteLine("Mammal Speaks"); } } class Dog : Mammal { public override void speak() { Console.WriteLine("Woof!"); } } class Cat : Mammal { public override void speak() { Console.WriteLine("Meow!"); } } class ConsoleApp { static void Main() { Mammal m1 = new Mammal(); Mammal m2 = new Cat(); Mammal m3 = new Dog(); m1.speak(); // Since it stores a Mammal() object, it executes the Mammal's implementation m2.speak(); // Output: Meow! m3.speak(); // Output: Woof! // The following behave the same as they did in the previous example: Dog d1 = new Dog(); d1.speak(); Cat c1 = new Cat(); c1.speak(); } }
Observe que na saída, m2
e m3
utilizam a implementação do método das classes Cat
e Dog
, pois esses objetos foram originalmente criados como Cat
e Dog
.
Esse processo de sobrescrever métodos para que se comportem de acordo com seu tipo original é chamado de polimorfismo.
Uma situação em que isso pode ser muito útil é a possibilidade de armazenar objetos de diferentes tipos de dados em um único array ou lista.
Por exemplo:
index.cs
123456789List<Shape> shapes = new List<Shape>(); shapes.Add(new Rectangle(25, 50)); shapes.Add(new Square(50)); shapes.Add(new Circle(10)); shapes[0].getArea(); // Output: 1250.0f shapes[1].getArea(); // Output: 2500.0f shapes[2].getArea(); // Output: 314.15f
1. Quais são as duas palavras-chave usadas na sobrescrita de métodos?
2. Qual é a forma correta de tornar um método sobrescrevível?
Obrigado pelo seu feedback!