Polimorfismo
El polimorfismo es un concepto fundamental en la programación orientada a objetos que permite que un objeto de una clase pueda ser tratado como un objeto de su clase base o de una de sus clases derivadas. El término “polimorfismo” proviene de las palabras griegas “poly” (muchos) y “morphos” (formas), lo que significa “muchas formas“.
En la programación orientada a objetos, el polimorfismo permite que un objeto pueda comportarse de diferentes maneras según el contexto en el que se utiliza. Por lo que objeto de una clase puede ser tratado como un objeto de una clase relacionada, proporcionando flexibilidad y extensibilidad en el diseño del software.
Características
- Herencia: Una clase derivada hereda los atributos y métodos de su clase base y puede agregar nuevos miembros o redefinir los existentes.
- Sustitución: Se establece que un objeto de una clase derivada puede ser utilizado donde se espera un objeto de su clase base. Esto se debe a que el objeto derivado comparte la misma interfaz o supertipo que el objeto base.
Los métodos polimórficos, son métodos declarados en una clase base y que pueden ser redefinidos en las clases derivadas. Cuando se invoca un método polimórfico a través de una referencia de la clase base, la implementación correcta del método se determina en tiempo de ejecución según el tipo real del objeto al que se hace referencia.
Permite escribir código más genérico y flexible, ya que se pueden definir comportamientos comunes en la clase base y aprovechar las implementaciones específicas en las clases derivadas. Además, facilita la extensibilidad del código, ya que se pueden agregar nuevas clases derivadas sin modificar el código existente que trabaja con la clase base.
Ejemplos
// Clase base (superclase) class Animal { public void hacerSonido() { System.out.println("El animal hace un sonido."); } } // Clases derivadas (subclases) class Perro extends Animal { public void hacerSonido() { System.out.println("El perro ladra."); } } class Gato extends Animal { public void hacerSonido() { System.out.println("El gato maulla."); } } // Clase principal public class Polimorfismo { public static void main(String[] args) { Animal animal1 = new Perro(); // Crear un objeto Perro y asignarlo a una referencia Animal Animal animal2 = new Gato(); // Crear un objeto Gato y asignarlo a una referencia Animal animal1.hacerSonido(); // Llamar al método hacerSonido() del objeto Perro animal2.hacerSonido(); // Llamar al método hacerSonido() del objeto Gato } }
En este ejemplo, tenemos una clase base llamada Animal
que tiene un método hacerSonido()
que muestra un mensaje genérico de que el animal hace un sonido.
Luego, tenemos dos clases derivadas: Perro
y Gato
. Estas clases heredan de la clase Animal
y redefinen el método hacerSonido()
para mostrar mensajes específicos de cada tipo de animal.
En el método PolimorfismoAnimal()
, creamos dos objetos: animal1
de tipo Perro
y animal2
de tipo Gato
. Aunque declaramos las referencias como tipo Animal
, los objetos reales son instancias de las clases derivadas.
Cuando llamamos al método hacerSonido()
en animal1
y animal2
, el comportamiento polimórfico entra en juego. La llamada al método se resuelve en tiempo de ejecución y se utiliza la implementación específica de cada clase derivada. Esto significa que el mensaje mostrado será “El perro ladra” para animal1
y “El gato maulla” para animal2
.
Otro Ejemplo
// Clase base (superclase) class Figura { public double calcularArea() { return 0.0; } } // Clase derivada Cuadrado class Cuadrado extends Figura { private double lado; public Cuadrado(double lado) { this.lado = lado; } @Override public double calcularArea() { return lado * lado; } } // Clase derivada Círculo class Circulo extends Figura { private double radio; public Circulo(double radio) { this.radio = radio; } @Override public double calcularArea() { return Math.PI * radio * radio; } } // Clase principal public class PolimorfismoArea { public static void main(String[] args) { Figura cuadrado = new Cuadrado(5.0); // Crear un objeto Cuadrado Figura circulo = new Circulo(3.0); // Crear un objeto Círculo // Calcular y mostrar el área del cuadrado System.out.println("Área del cuadrado: " + cuadrado.calcularArea()); // Calcular y mostrar el área del círculo System.out.println("Área del círculo: " + circulo.calcularArea()); } }
En este ejemplo, la clase base Figura
tiene un método calcularArea()
que devuelve un valor predeterminado de 0.0. Las clases derivadas Cuadrado
y Círculo
heredan de la clase base y sobrescriben el método calcularArea()
con sus propias implementaciones.
En el método PolimorfismoArea()
, Se crean dos objetos cuadrado
y circulo
de tipo Figura
, pero utilizando las clases derivadas Cuadrado
y Circulo
. Cuando llamamos al método calcularArea()
en estos objetos, el polimorfismo entra en juego y se ejecuta la implementación específica de cada clase derivada.
No se debe olvidar que @Override
permite indicar que un método de una clase está sobrescribiendo (override) un método de su clase base (superclase) o interfaz; es opcional, pero se recomienda su uso por las siguientes razones:
- Verificación de errores: Ayuda al compilador a verificar si el método realmente está sobrescribiendo un método existente en la clase base o interfaz. Si no está sobrescribiendo ningún método válido, el compilador generará un error de compilación.
- Legibilidad: Queda claro para los desarrolladores que el método en la clase derivada está diseñado para reemplazar o sobrescribir el comportamiento del método en la clase base o interfaz.
- Mantenimiento: Si en algún momento se realiza una modificación en la clase base o interfaz que afecte a los métodos que se están sobrescribiendo, el compilador generará un error si se olvida actualizar el método correspondiente en la clase derivada. Ayudando a evitar errores sutiles y el código actualizado.
En el vídeo 1. se puede apreciar la ejecución de los dos códigos expuesto en este post. Espero que este post ayude a comprender mejor el concepto de polimorfismo en java.