CodeGym /Cursos /JAVA 25 SELF /Errores con la herencia y la sobrecarga de métodos

Errores con la herencia y la sobrecarga de métodos

JAVA 25 SELF
Nivel 23 , Lección 1
Disponible

1. Errores al heredar

La herencia es uno de los pilares de la POO, pero también uno de los temas en los que los desarrolladores principiantes tropiezan con más frecuencia. Veamos los errores clásicos y aprendamos a evitarlos.

Falta de llamada al constructor de la clase base (super(...))

Cuando creas una subclase, es importante recordar que la clase base puede requerir cierta inicialización mediante su constructor. Si en la clase base no hay un constructor por defecto (sin parámetros), entonces en el constructor de la subclase es obligatorio invocar explícitamente el constructor de la clase base con super(...).

Ejemplo de error:

class Animal {
    private String name;
    public Animal(String name) {
        this.name = name;
    }
}

class Dog extends Animal {
    // ¡Error! Animal no tiene constructor por defecto
    public Dog() {
        // super(); // el compilador inserta super() automáticamente, pero ¡ese constructor no existe!
    }
}

Cómo corregirlo:

class Dog extends Animal {
    public Dog(String name) {
        super(name); // ¡Todo bien!
    }
}

Comentario:
Si en la clase base solo hay constructores con parámetros, el compilador no añadirá automáticamente un constructor sin parámetros. Esta es una causa frecuente de errores de compilación.

Intentar heredar de una clase final o sobrescribir un método final

En Java puedes declarar una clase o un método como final. Esto significa:

  • No se puede heredar de la clase.
  • No se puede sobrescribir (override) el método en las subclases.

Ejemplo de error:

final class Cat {}

// ¡Error de compilación!
class Tiger extends Cat { 
    // ...
}
class Animal {
    public final void sleep() {
        System.out.println("Zzz...");
    }
}

class Dog extends Animal {
    // ¡Error de compilación!
    @Override
    public void sleep() {
        System.out.println("Dog is sleeping...");
    }
}

Comentario:
Si ves el error «cannot inherit from final», «cannot override final method», ¡revisa los modificadores!

Incumplimiento del principio de sustitución de Liskov (Liskov Substitution Principle)

Suena complejo, pero en la práctica significa: un objeto de una subclase debe comportarse como un objeto de la clase base, sin romper la lógica del programa. Un error habitual es sobrescribir métodos de tal manera que la nueva clase se comporte de forma distinta a lo esperado por la clase base.

Ejemplo:

class Bird {
    public void fly() {
        System.out.println("¡Estoy volando!");
    }
}

class Penguin extends Bird {
    @Override
    public void fly() {
        throw new UnsupportedOperationException("Los pingüinos no vuelan");
    }
}

¿Cuál es el problema?
El código que trabaja con Bird espera que cualquier ave pueda volar. Pero si le pasas un Penguin, el programa puede fallar.

Mejor enfoque:
En estos casos conviene replantear la jerarquía o usar interfaces/composición.

2. Errores con la sobrecarga de métodos (overloading)

La sobrecarga es cuando en una misma clase hay varios métodos con el mismo nombre pero con parámetros diferentes. Parece sencillo, pero aquí también hay trampas.

Sobrecarga en lugar de sobrescritura (error en la firma)

A menudo, los principiantes quieren sobrescribir (override) un método de la clase base, pero cambian accidentalmente sus parámetros. Como resultado, se obtiene una sobrecarga, no una sobrescritura, ¡y el polimorfismo no funciona!

Ejemplo de error:

class Animal {
    public void makeSound() {
        System.out.println("Some sound");
    }
}

class Dog extends Animal {
    // Querían sobrescribir, pero hicieron una sobrecarga
    public void makeSound(String extra) {
        System.out.println("Bark! " + extra);
    }
}

Problema:
La llamada dog.makeSound() invocará el método padre, no tu método nuevo.
La llamada dog.makeSound("loudly") invocará el sobrecargado, ¡pero el polimorfismo no funciona!

Mejor práctica:
Usa la anotación @Override: si te equivocas en la firma, el compilador te avisará al instante.

@Override
public void makeSound() { /* ... */ }

Comportamiento no obvio en la sobrecarga (conversión automática de tipos, ambigüedad en la llamada)

A veces Java puede «elegir» un método distinto al que esperabas si los parámetros coinciden con varias sobrecargas.

public class OverloadDemo {
    public void print(int x) {
        System.out.println("int: " + x);
    }
    public void print(double x) {
        System.out.println("double: " + x);
    }
}

OverloadDemo demo = new OverloadDemo();
demo.print(5);     // int: 5
demo.print(5.0);   // double: 5.0
demo.print(5L);    // long -> double: double: 5.0

Problema:
Si llamas a demo.print(5L), Java elegirá print(double x) (ya que long se convierte mejor a double que a int).
Si existen métodos con parámetros de tipo Object, Integer, int, una llamada con null puede provocar un error de compilación: «reference to print is ambiguous».

Usar el mismo nombre de método con distinto tipo de retorno (error de compilación)

En Java no puedes declarar dos métodos con el mismo nombre y la misma lista de parámetros que solo difieran en el tipo de retorno.

public class Demo {
    // Error de compilación
    public int foo() { return 1; }
    public String foo() { return "hello"; }
}

Explicación:
La firma de un método para sobrecarga es nombre + parámetros. El tipo de retorno no se tiene en cuenta. El compilador no podrá entender qué método quieres invocar.

3. Mejores prácticas

Para evitar tropiezos con la herencia y la sobrecarga, sigue estas recomendaciones:

Usa siempre la anotación @Override en los métodos que vayas a sobrescribir

No solo mejora la legibilidad, también te protege de errores en la firma. Si cambias accidentalmente los parámetros o el nombre del método, el compilador te avisará de inmediato.

@Override
public void makeSound() {
    System.out.println("Bark!");
}

Distingue claramente entre sobrecarga y sobrescritura

  • Sobrescritura (override): cambias el comportamiento del método del padre; la firma debe coincidir.
  • Sobrecarga (overload): añades un método nuevo con el mismo nombre, pero con parámetros distintos.

Tabla ilustrativa:

Sobrecarga (overloading) Sobrescritura (overriding)
Dónde En una misma clase/jerarquía En la subclase
Nombre del método Coincide Coincide
Parámetros Diferentes Coinciden
Valor de retorno Puede diferir Debe coincidir/ser compatible
Anotación No es necesaria Se recomienda @Override

No abuses de la sobrecarga

Si un método tiene demasiadas variantes sobrecargadas, el código se vuelve ilegible y confuso. Es mejor usar objetos de parámetros o el patrón Builder si hay demasiadas variantes.

4. Ejemplo: sistema de registro de mascotas

Supón que haces un sistema sencillo de registro de mascotas. Tienes una clase base Pet y las subclases Cat y Dog.

public class Pet {
    private String name;
    public Pet(String name) {
        this.name = name;
    }
    public void speak() {
        System.out.println(name + " emite un sonido indescifrable.");
    }
}

public class Cat extends Pet {
    public Cat(String name) {
        super(name);
    }
    @Override
    public void speak() {
        System.out.println(getName() + " dice: ¡Miau!");
    }
    // Error: no existe el método getName() en Pet
}

Error típico:
Al intentar sobrescribir un método, accedemos a un método que no existe en la clase base. Es mejor añadir un getter:

public class Pet {
    private String name;
    public Pet(String name) { this.name = name; }
    public String getName() { return name; }
    public void speak() { System.out.println(name + " emite un sonido indescifrable."); }
}

Ahora todo funciona correctamente y podemos usar polimorfismo:

Pet myPet = new Cat("Rex");
myPet.speak(); // Rex dice: ¡Miau!

5. Errores típicos con herencia y sobrecarga

Error n.º 1: olvidaste llamar a super(...).
Si la clase base tiene lógica importante en el constructor y no la llamas, el programa puede comportarse de forma inesperada o ni siquiera compilar.

Error n.º 2: sobrescribiste el método equivocado.
Querías cambiar el comportamiento del método del padre, pero en realidad añadiste un método nuevo con un nombre parecido o con otros parámetros. Resultado: el método antiguo sigue funcionando como antes y tu método nuevo no lo llama nadie.

Error n.º 3: intentaste sobrescribir un método final.
Java no te permitirá hacerlo (¡y está bien!). Si ves un error de compilación, busca final.

Error n.º 4: sobrecargaste el método hasta hacerlo irreconocible.
Cuando tienes 10 variantes del método calculate y tú mismo te confundes sobre cuál se invoca, es hora de parar y pensar en refactorizar.

Error n.º 5: violaste el principio de Liskov.
Si tu subclase cambia el significado del comportamiento de la clase base, toda la arquitectura puede «descarrilar». Por ejemplo, si tienes una clase Shape con el método getArea() y una subclase BrokenShape devuelve -1, esto puede provocar bugs extraños.

1
Tarea
JAVA 25 SELF, nivel 23, lección 1
Bloqueada
La inmutabilidad fundamental del mundo animal 🌳
La inmutabilidad fundamental del mundo animal 🌳
1
Tarea
JAVA 25 SELF, nivel 23, lección 1
Bloqueada
Orquesta canina: Extensión de sonidos, no reemplazo 🐶
Orquesta canina: Extensión de sonidos, no reemplazo 🐶
1
Tarea
JAVA 25 SELF, nivel 23, lección 1
Bloqueada
Requisito estricto de nombre para cada mascota 🐱
Requisito estricto de nombre para cada mascota 🐱
1
Tarea
JAVA 25 SELF, nivel 23, lección 1
Bloqueada
Sistema inteligente de registro de números 📊
Sistema inteligente de registro de números 📊
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION