CodeGym /Cours /JAVA 25 SELF /Erreurs avec l’héritage et la surcharge de méthodes

Erreurs avec l’héritage et la surcharge de méthodes

JAVA 25 SELF
Niveau 23, Leçon 1
Disponible

1. Erreurs liées à l’héritage

L’héritage est l’un des piliers de l’OOP, mais aussi l’un des sujets sur lesquels les débutants se prennent le plus les pieds dans le tapis. Voyons les erreurs classiques et apprenons à les éviter.

Absence d’appel au constructeur de la classe de base (super(...))

Lorsque vous créez une sous-classe, il est important de se rappeler : la classe de base peut exiger une initialisation spécifique via son constructeur. S’il n’y a pas de constructeur par défaut (sans paramètres) dans la classe de base, alors dans le constructeur de la classe dérivée il faut obligatoirement appeler explicitement le constructeur de la classe de base à l’aide de super(...).

Exemple d’erreur :

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

class Dog extends Animal {
    // Erreur ! Pas de constructeur par défaut dans Animal
    public Dog() {
        // super(); // le compilateur insère super() automatiquement, mais un tel constructeur n’existe pas !
    }
}

Comment corriger :

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

Commentaire :
Si la classe de base ne possède qu’un constructeur avec paramètres, le compilateur n’ajoutera pas automatiquement de constructeur sans paramètres. C’est une cause fréquente d’erreurs de compilation.

Tentative d’hériter d’une classe final ou de redéfinir une méthode final

En Java, on peut déclarer une classe ou une méthode comme final. Cela signifie :

  • La classe ne peut pas être héritée.
  • La méthode ne peut pas être redéfinie (override) dans les sous-classes.

Exemple d’erreur :

final class Cat {}

// Erreur de compilation !
class Tiger extends Cat { 
    // ...
}
class Animal {
    public final void sleep() {
        System.out.println("Zzz...");
    }
}

class Dog extends Animal {
    // Erreur de compilation !
    @Override
    public void sleep() {
        System.out.println("Dog is sleeping...");
    }
}

Commentaire :
Si vous voyez l’erreur "cannot inherit from final", "cannot override final method" — vérifiez les modificateurs !

Violation du principe de substitution de Liskov (Liskov Substitution Principle)

Cela peut sembler intimidant, mais en pratique cela signifie : un objet d’une sous-classe doit se comporter comme un objet de la classe de base, sans casser la logique du programme. Une erreur fréquente consiste à redéfinir des méthodes de sorte que la nouvelle classe ne se comporte pas comme attendu pour la classe de base.

Exemple :

class Bird {
    public void fly() {
        System.out.println("Je vole!");
    }
}

class Penguin extends Bird {
    @Override
    public void fly() {
        throw new UnsupportedOperationException("Les manchots ne volent pas!");
    }
}

Où est le problème ?
Le code qui travaille avec Bird s’attend à ce que tout oiseau sache voler. Mais si on lui passe un Penguin, le programme peut s’effondrer.

Meilleure approche :
Dans de tels cas, il vaut mieux repenser l’architecture d’héritage ou utiliser des interfaces/composition.

2. Erreurs liées à la surcharge de méthodes (overloading)

La surcharge consiste à avoir, dans une même classe, plusieurs méthodes portant le même nom mais avec des paramètres différents. Cela paraît simple, mais il y a des pièges.

Surcharge au lieu de redéfinition (erreur de signature)

Souvent, les débutants veulent redéfinir (override) une méthode de la classe de base, mais en modifient accidentellement ses paramètres. Au final, on obtient une surcharge et non une redéfinition — et le polymorphisme ne fonctionne pas !

Exemple d’erreur :

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

class Dog extends Animal {
    // On voulait redéfinir, mais on a fait une surcharge !
    public void makeSound(String extra) {
        System.out.println("Bark! " + extra);
    }
}

Problème :
L’appel dog.makeSound() invoquera la méthode parente, et non votre nouvelle méthode.
L’appel dog.makeSound("loudly") invoquera la méthode surchargée, mais le polymorphisme ne fonctionne pas !

Bonne pratique :
Utilisez l’annotation @Override — si vous vous trompez dans la signature, le compilateur vous le signalera immédiatement.

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

Comportement non évident lors de la surcharge (conversion automatique des types, ambiguïté d’appel)

Java peut parfois « choisir » une méthode différente de celle attendue si les paramètres correspondent à plusieurs surcharges par le type.

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

Problème :
Si vous appelez demo.print(5L), Java choisira print(double x) (car long se convertit plus facilement en double qu’en int).
S’il existe des méthodes avec des paramètres de type Object, Integer, int, un appel avec null peut provoquer une erreur de compilation : « reference to print is ambiguous ».

Utiliser des noms de méthodes identiques avec des types de retour différents (erreur de compilation)

En Java, on ne peut pas déclarer deux méthodes ayant le même nom et la même liste de paramètres, mais qui ne diffèrent que par le type de retour !

public class Demo {
    // Erreur de compilation !
    public int foo() { return 1; }
    public String foo() { return "hello"; }
}

Explication :
La signature d’une méthode pour la surcharge correspond à son nom + ses paramètres. Le type de retour n’est pas pris en compte. Le compilateur ne pourra pas déterminer quelle méthode vous souhaitez appeler.

3. Bonnes pratiques

Pour éviter les pièges de l’héritage et de la surcharge, suivez ces recommandations :

Utilisez toujours l’annotation @Override pour les méthodes redéfinies

Cela améliore non seulement la lisibilité, mais protège aussi des erreurs de signature. Si vous modifiez par inadvertance les paramètres ou le nom de la méthode, le compilateur vous en informera immédiatement.

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

Faites bien la différence entre surcharge et redéfinition

  • Redéfinition (override) : vous modifiez le comportement de la méthode parente — la signature doit correspondre.
  • Surcharge (overload) : vous ajoutez une nouvelle méthode avec le même nom, mais des paramètres différents.

Tableau récapitulatif :

Surcharge (overloading) Redéfinition (overriding)
Où Dans une même classe/hiérarchie Dans une sous-classe
Nom de la méthode Identique Identique
Paramètres Différents Identiques
Retour Peut différer Doit être identique/compatible
Annotation Non requise @Override recommandée

N’abusez pas de la surcharge

Si une méthode possède trop de variantes surchargées, le code devient illisible et confus. Il vaut mieux utiliser des objets de paramètres ou le pattern Builder si les variantes sont trop nombreuses.

4. Exemple : système de gestion des animaux de compagnie

Supposons que vous réalisiez un simple système de gestion des animaux de compagnie. Vous avez une classe de base Pet et des classes dérivées Cat et Dog.

public class Pet {
    private String name;
    public Pet(String name) {
        this.name = name;
    }
    public void speak() {
        System.out.println(name + " émet un son incompréhensible.");
    }
}

public class Cat extends Pet {
    public Cat(String name) {
        super(name);
    }
    @Override
    public void speak() {
        System.out.println(getName() + " dit: Miaou!");
    }
    // Erreur: la méthode getName() n’existe pas dans Pet !
}

Erreur typique :
En voulant redéfinir une méthode, on appelle une méthode qui n’existe pas dans la classe de base. Mieux vaut ajouter un accesseur (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 + " émet un son incompréhensible."); }
}

À présent, tout fonctionne correctement et nous pouvons utiliser le polymorphisme :

Pet myPet = new Cat("Whiskers");
myPet.speak(); // Whiskers dit: Miaou!

5. Erreurs typiques avec l’héritage et la surcharge

Erreur n° 1 : oubli d’appeler super(...).
Si la classe de base contient une logique importante dans son constructeur mais que vous ne l’appelez pas, le programme peut se comporter de manière inattendue, voire ne pas se compiler.

Erreur n° 2 : redéfinition de la mauvaise méthode.
Vous vouliez modifier le comportement d’une méthode parente, mais vous avez en réalité ajouté une nouvelle méthode avec un nom semblable ou des paramètres différents. Résultat : l’ancienne méthode continue de fonctionner comme avant, et votre nouvelle méthode n’est appelée par personne.

Erreur n° 3 : tentative de redéfinir une méthode final.
Java ne vous laissera pas faire — et c’est tant mieux ! Mais si vous voyez une erreur de compilation, cherchez du côté de final.

Erreur n° 4 : surcharge à outrance.
Quand vous avez 10 variantes de la méthode calculate et que vous vous emmêlez sur celle qui est appelée — il est temps de s’arrêter et de penser à un refactoring.

Erreur n° 5 : violation du principe de Liskov.
Si votre sous-classe change le sens du comportement de la classe de base, toute l’architecture peut « déraper ». Par exemple, si vous avez une classe Shape avec une méthode getArea(), et qu’une sous-classe BrokenShape renvoie -1, cela peut entraîner des bugs surprenants.

1
Mission
JAVA 25 SELF, niveau 23, leçon 1
Bloqué
L'immuabilité fondamentale du règne animal 🌳
L'immuabilité fondamentale du règne animal 🌳
1
Mission
JAVA 25 SELF, niveau 23, leçon 1
Bloqué
Orchestre canin : étendre les sons, pas les remplacer 🐶
Orchestre canin : étendre les sons, pas les remplacer 🐶
1
Mission
JAVA 25 SELF, niveau 23, leçon 1
Bloqué
Exigence stricte de nom pour chaque animal de compagnie 🐱
Exigence stricte de nom pour chaque animal de compagnie 🐱
1
Mission
JAVA 25 SELF, niveau 23, leçon 1
Bloqué
Système intelligent de journalisation des nombres 📊
Système intelligent de journalisation des nombres 📊
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION