CodeGym /Cours /JAVA 25 SELF /Redéfinition de méthodes (override), annotation @Override...

Redéfinition de méthodes (override), annotation @Override

JAVA 25 SELF
Niveau 17 , Leçon 1
Disponible

1. Redéfinition de méthode

Dans la vie, il arrive souvent qu’un « héritier » se comporte de façon particulière. Par exemple, tous les animaux savent émettre des sons, mais le chat fait « miaou », le chien « ouaf », et le développeur « aïe, encore un bug ! ». En programmation, cela se réalise via la redéfinition de méthode (override).

La redéfinition de méthode, c’est lorsqu’une sous-classe fournit sa propre implémentation d’une méthode déjà déclarée dans la classe parente. Autrement dit, elle « remplace » le comportement standard par le sien, plus spécifique.

Analogie. Si l’on imagine la classe parente comme une recette signature de borchtch, alors redéfinir une méthode revient à ce que la grand‑mère y ajoute son ingrédient secret. Le borchtch reste du borchtch, mais chacun a son goût.

Pour redéfinir une méthode, il faut déclarer dans la classe fille une méthode avec exactement la même signature (nom, paramètres, type de retour) que celle du parent.

Exemple : animaux et leurs sons

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

class Dog extends Animal {
    // On redéfinit la méthode makeSound()
    void makeSound() {
        System.out.println("Woof!");
    }
}

class Cat extends Animal {
    // On redéfinit la méthode makeSound()
    void makeSound() {
        System.out.println("Meow!");
    }
}

Désormais, si vous créez un objet Dog et appelez makeSound(), vous entendrez "Woof!", et non "Some generic animal sound".

Démonstration en code

public class Main {
    public static void main(String[] args) {
        Animal generic = new Animal();
        Dog dog = new Dog();
        Cat cat = new Cat();

        generic.makeSound(); // Some generic animal sound
        dog.makeSound();     // Woof!
        cat.makeSound();     // Meow!
    }
}

Important : si la sous-classe n’a pas de méthode avec la même signature, c’est la méthode du parent qui sera utilisée.

2. Annotation @Override : à quoi elle sert et comment l’utiliser

En Java, on a l’habitude de marquer les méthodes redéfinies avec l’annotation @Override. Ce n’est pas qu’un embellissement du code, c’est un outil utile :

  • Le compilateur vérifie que vous redéfinissez bien une méthode du parent. Si vous vous trompez dans le nom, le type d’un paramètre ou le type de retour, le compilateur signalera une erreur.
  • Améliore la lisibilité du code. Un autre développeur voit immédiatement : « Ah, cette méthode redéfinit celle du parent ».

Exemple avec @Override

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Woof!");
    }
}

Si vous écrivez par erreur void makeSond() (typo !) dans une méthode annotée @Override, le compilateur se plaindra : "Method does not override or implement a method from a supertype".

Normes actuelles. Utiliser @Override est une bonne pratique et un standard de l’industrie. Même si le compilateur ne l’exige pas, mettez toujours cette annotation : cela simplifiera la vie pour vous et vos collègues.

3. Comment fonctionne l’appel d’une méthode redéfinie

Quand vous appelez une méthode sur un objet d’une sous-classe, c’est l’implémentation de la sous-classe qui est utilisée, même si la variable est déclarée avec le type parent.

Exemple : polymorphisme en action

Animal animal = new Dog();
animal.makeSound(); // "Woof!", et non "Some generic animal sound"

Ici, la variable est de type Animal, mais elle référence en réalité un objet Dog. Java « comprend » qu’il faut appeler la méthode redéfinie de Dog. C’est ce qu’on appelle le polymorphisme (plus de détails dans les prochains cours).

4. Règles et limitations de la redéfinition

Signature de la méthode

  • Le nom, le type et l’ordre des paramètres doivent correspondre à ceux de la méthode du parent.
  • Le type de retour doit être identique ou covariant (un sous-type du type de retour du parent). Par exemple, si le parent retourne Animal et l’enfant retourne Dog, c’est autorisé.

Modificateurs d’accès

  • On ne peut pas rendre l’accès plus strict que dans le parent.
  • Si la méthode parente est public, la méthode redéfinie doit aussi être public.
  • Si la méthode parente est protected, la méthode redéfinie peut être protected ou public.

Si vous essayez de faire l’inverse, le compilateur dira : "Cannot reduce the visibility of the inherited method".

Exceptions

  • La méthode redéfinie ne peut pas lancer une nouvelle exception contrôlée (checked) qui n’est pas déclarée dans le parent.
  • Elle peut lancer moins d’exceptions que le parent, ou des sous-types de celles-ci.

static, final, private

  • On ne peut pas redéfinir les méthodes déclarées static ou final, ni les méthodes privées (private).
  • static correspond à un masquage (hiding), pas à une redéfinition.
  • final ne peut pas être redéfini : Java protège de telles méthodes.
  • private n’est pas visible dans la classe fille, on ne peut pas la redéfinir (on peut seulement déclarer une nouvelle méthode du même nom).

Constructeurs

Les constructeurs ne sont ni hérités ni redéfinis. Chaque classe a ses propres constructeurs.

5. Faisons évoluer l’application pédagogique « Zoo »

Il est temps d’appliquer la théorie ! Poursuivons le développement de notre application « Zoo ».

Étape 1. Classe de base Animal

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

    public void sleep() {
        System.out.println("Zzz...");
    }
}

Étape 2. Sous-classes Dog et Cat

public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof!");
    }

    // Méthode supplémentaire propre à Dog
    public void fetch() {
        System.out.println("Dog brings the stick!");
    }
}

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow!");
    }

    // Méthode supplémentaire propre à Cat
    public void scratch() {
        System.out.println("Cat scratches the sofa!");
    }
}

Étape 3. Utiliser la redéfinition

public class ZooTest {
    public static void main(String[] args) {
        Animal generic = new Animal();
        Animal dog = new Dog();
        Animal cat = new Cat();

        generic.makeSound(); // Some generic animal sound
        dog.makeSound();     // Woof!
        cat.makeSound();     // Meow!

        // dog.fetch(); // Erreur ! La variable de type Animal ne connaît pas fetch()
        // cat.scratch(); // De même

        // Mais si on précise explicitement le type :
        if (dog instanceof Dog) {
            ((Dog) dog).fetch(); // Dog brings the stick!
        }
        if (cat instanceof Cat) {
            ((Cat) cat).scratch(); // Cat scratches the sofa!
        }
    }
}

Commentaire :
La méthode makeSound() fonctionne de manière polymorphe : c’est la version de la classe réelle de l’objet qui est appelée. En revanche, les méthodes spécifiques (fetch, scratch) ne sont accessibles qu’avec un transtypage explicite — c’est important pour comprendre comment fonctionnent l’héritage et la redéfinition.

6. Exemple avec type de retour (covariance)

Parfois, on souhaite que la méthode redéfinie retourne un type plus « étroit ». Par exemple :

class Animal {
    Animal getFriend() {
        return new Animal();
    }
}

class Dog extends Animal {
    @Override
    Dog getFriend() { // Type de retour : Dog, sous‑type de Animal
        return new Dog();
    }
}

Cela s’appelle la covariance du type de retour et c’est autorisé en Java (depuis Java 5).

7. Que se passe‑t‑il si l’on n’utilise pas @Override ?

Si vous vous trompez par inadvertance dans le nom de la méthode ou ses paramètres, Java ne se plaindra pas en l’absence de l’annotation @Override. Au final, vous ne redéfinirez pas, mais créerez une nouvelle méthode, et le comportement attendu ne changera pas.

Exemple d’erreur

class Dog extends Animal {
    // Faute de frappe : makeSoud au lieu de makeSound
    void makeSoud() {
        System.out.println("Woof!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.makeSound(); // Affichera "Some generic animal sound"
    }
}

S’il y avait @Override, le compilateur signalerait une erreur : "Method does not override or implement a method from a supertype".

8. Erreurs typiques lors de la redéfinition de méthodes

Erreur n° 1 : absence de l’annotation @Override.
Sans elle, on se trompe facilement dans le nom de la méthode ou les paramètres. En conséquence, la méthode ne sera pas redéfinie, et le programme ne se comportera pas comme prévu.

Erreur n° 2 : tentative de restreindre le modificateur d’accès.
Si la méthode parente est public et que vous écrivez protected ou private, vous obtiendrez une erreur de compilation.

Erreur n° 3 : signature non correspondante.
Si les paramètres diffèrent ne serait‑ce que par le type, ce n’est plus une redéfinition, mais une surcharge (overloading).

Erreur n° 4 : tentative de redéfinir une méthode final ou static.
Java ne l’autorisera pas : final protège de la redéfinition, et les méthodes static ne sont pas redéfinies (elles ne font que masquer).

Erreur n° 5 : modification du type de retour vers un type incompatible.
On ne peut retourner qu’un sous-type du type de retour du parent (covariance), pas un type complètement différent.

Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION