Polimorfismo y Redefinición de Métodos en Java

El polimorfismo es uno de los pilares fundamentales de la programación orientada a objetos. Permite que una referencia a una clase pueda comportarse de diferentes maneras dependiendo del objeto que se asocie. En este artículo, exploraremos cómo Java implementa polimorfismo a través de la herencia y la redefinición de métodos.

1. ¿Qué es el polimorfismo?

El término «polimorfismo» significa muchas formas. En Java, esto implica que:

  1. Una misma referencia de tipo padre puede apuntar a objetos de diferentes subclases.
  2. Los métodos de una subclase pueden sobrescribir el comportamiento de los métodos heredados.

2. Polimorfismo a través de herencia

Ejemplo básico:

public class Animal {
    public void hacerSonido() {
        System.out.println("El animal hace un sonido.");
    }
}

public class Perro extends Animal {
    @Override
    public void hacerSonido() {
        System.out.println("El perro ladra.");
    }
}

public class Gato extends Animal {
    @Override
    public void hacerSonido() {
        System.out.println("El gato maúlla.");
    }
}

public class Principal {
    public static void main(String[] args) {
        Animal miAnimal;

        miAnimal = new Perro();
        miAnimal.hacerSonido(); // El perro ladra.

        miAnimal = new Gato();
        miAnimal.hacerSonido(); // El gato maúlla.
    }
}

Salida:

El perro ladra.
El gato maúlla.

3. Redefinición de métodos (@Override)

La redefinición (o sobrescritura) permite que una subclase modifique el comportamiento de un método heredado. Para sobrescribir correctamente un método, sigue estas reglas:

  1. Misma firma: El nombre, tipo de retorno y parámetros deben coincidir exactamente.
  2. Compatibilidad con modificadores: No puedes hacer el método más restrictivo en la subclase.
  3. Usa la anotación @Override: Ayuda a evitar errores y hace el código más legible.

Ejemplo de redefinición:

public class Vehiculo {
    public void arrancar() {
        System.out.println("El vehículo está arrancando.");
    }
}

public class Coche extends Vehiculo {
    @Override
    public void arrancar() {
        System.out.println("El coche está arrancando.");
    }
}

4. Llamar al método original con super

Si necesitas acceder al método de la superclase desde la subclase, usa super.

Ejemplo práctico:

public class Vehiculo {
    public void arrancar() {
        System.out.println("El vehículo está arrancando.");
    }
}

public class Moto extends Vehiculo {
    @Override
    public void arrancar() {
        super.arrancar();
        System.out.println("La moto está lista para rodar.");
    }
}

public class Principal {
    public static void main(String[] args) {
        Moto miMoto = new Moto();
        miMoto.arrancar();
    }
}

Salida:

El vehículo está arrancando.
La moto está lista para rodar.

5. Polimorfismo y tipos genéricos

El polimorfismo también permite almacenar diferentes tipos de objetos en estructuras genéricas, como listas.

Ejemplo con listas:

import java.util.ArrayList;

public class Principal {
    public static void main(String[] args) {
        ArrayList<Animal> animales = new ArrayList<>();

        animales.add(new Perro());
        animales.add(new Gato());

        for (Animal animal : animales) {
            animal.hacerSonido();
        }
    }
}

Salida:

El perro ladra.
El gato maúlla.

6. Polimorfismo y clases abstractas

Las clases abstractas y las interfaces son herramientas clave para implementar polimorfismo.

Ejemplo con una clase abstracta:

public abstract class Figura {
    public abstract double calcularArea();
}

public class Circulo extends Figura {
    private double radio;

    public Circulo(double radio) {
        this.radio = radio;
    }

    @Override
    public double calcularArea() {
        return Math.PI * radio * radio;
    }
}

public class Rectangulo extends Figura {
    private double base, altura;

    public Rectangulo(double base, double altura) {
        this.base = base;
        this.altura = altura;
    }

    @Override
    public double calcularArea() {
        return base * altura;
    }
}

public class Principal {
    public static void main(String[] args) {
        Figura miFigura;

        miFigura = new Circulo(5);
        System.out.println("Área del círculo: " + miFigura.calcularArea());

        miFigura = new Rectangulo(4, 6);
        System.out.println("Área del rectángulo: " + miFigura.calcularArea());
    }
}

Salida:

Área del círculo: 78.53981633974483
Área del rectángulo: 24.0

7. Ventajas del polimorfismo

  1. Flexibilidad:
    Puedes escribir código que funcione con diferentes tipos de objetos.
  2. Reutilización de código:
    Reduce duplicación al usar métodos genéricos.
  3. Mantenimiento:
    Agregar nuevas subclases no requiere cambios en el código existente que usa polimorfismo.

8. Errores comunes con polimorfismo

Llamar métodos no definidos en la superclase:
Solo puedes usar métodos que existen en la superclase.

Animal miAnimal = new Perro();
miAnimal.ladrar(); // Error: "ladrar" no está definido en Animal

Olvidar usar @Override:
Si no lo usas, Java no detectará errores en la firma del método redefinido.

Resumen

  • El polimorfismo permite trabajar con objetos de diferentes subclases a través de una referencia común.
  • Usa @Override para redefinir métodos heredados.
  • Aprovecha las clases abstractas e interfaces para diseñar sistemas polimórficos más robustos.