CodeGym/Blog Java/Random-FR/Polymorphisme Java
Auteur
Jesse Haniel
Lead Software Architect at Tribunal de Justiça da Paraíba

Polymorphisme Java

Publié dans le groupe Random-FR
membres
Les questions liées à la POO font partie intégrante de l'entretien technique pour un poste de développeur Java dans une société informatique. Dans cet article, nous parlerons d'un principe de la POO - le polymorphisme. Nous nous concentrerons sur les aspects qui sont souvent évoqués lors des entretiens, et donnerons également quelques exemples pour plus de clarté.

Qu'est-ce que le polymorphisme en Java ?

Le polymorphisme est la capacité d'un programme à traiter des objets avec la même interface de la même manière, sans information sur le type spécifique de l'objet. Si vous répondez à une question sur ce qu'est le polymorphisme, il vous sera très probablement demandé d'expliquer ce que vous vouliez dire. Sans déclencher un tas de questions supplémentaires, expliquez à nouveau tout à l'intervieweur. Temps d'interview : polymorphisme en Java - 1Vous pouvez commencer par le fait que l'approche OOP consiste à construire un programme Java basé sur l'interaction entre des objets, qui sont basés sur des classes. Les classes sont des plans préalablement écrits (modèles) utilisés pour créer des objets dans le programme. De plus, une classe a toujours un type spécifique qui, avec un bon style de programmation, a un nom qui suggère son objectif. De plus, on peut noter que puisque Java est fortement typé, le code du programme doit toujours spécifier un type d'objet lorsque des variables sont déclarées. Ajoutez à cela le fait qu'un typage strict améliore la sécurité et la fiabilité du code, et permet, même à la compilation, d'éviter les erreurs dues à des types d'incompatibilité (par exemple, essayer de diviser une chaîne par un nombre). Naturellement, le compilateur doit "savoir" le type déclaré - il peut s'agir d'une classe du JDK ou d'une classe que nous avons créée nous-mêmes. Précisez à l'enquêteur que notre code peut utiliser non seulement les objets du type indiqué dans la déclaration mais aussi ses descendants.C'est un point important : nous pouvons travailler avec de nombreux types différents comme un seul type (à condition que ces types soient dérivés d'un type de base). Cela signifie également que si nous déclarons une variable dont le type est une superclasse, nous pouvons affecter une instance de l'un de ses descendants à cette variable. L'intervieweur appréciera si vous donnez un exemple. Sélectionnez une classe qui pourrait être partagée par (une classe de base pour) plusieurs classes et faites-en hériter quelques-unes d'entre elles. Classe de base :
public class Dancer {
    private String name;
    private int age;

    public Dancer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void dance() {
        System.out.println(toString() + " I dance like everyone else.");
    }

    @Override
    public String toString() {
        Return "I'm " + name + ". I'm " + age + " years old.";
    }
}
Dans les sous-classes, remplacez la méthode de la classe de base :
public class ElectricBoogieDancer extends Dancer {
    public ElectricBoogieDancer(String name, int age) {
        super(name, age);
    }
// Override the method of the base class
    @Override
    public void dance() {
        System.out.println(toString () + " I dance the electric boogie!");
    }
}

public class Breakdancer extends Dancer {

    public Breakdancer(String name, int age) {
        super(name, age);
    }
// Override the method of the base class
    @Override
    public void dance() {
        System.out.println(toString() + " I breakdance!");
    }
}
Un exemple de polymorphisme et comment ces objets pourraient être utilisés dans un programme :
public class Main {

    public static void main(String[] args) {
        Dancer dancer = new Dancer("Fred", 18);

        Dancer breakdancer = new Breakdancer("Jay", 19); // Widening conversion to the base type
        Dancer electricBoogieDancer = new ElectricBoogieDancer("Marcia", 20); // Widening conversion to the base type

        List<dancer> disco = Arrays.asList(dancer, breakdancer, electricBoogieDancer);
        for (Dancer d : disco) {
            d.dance(); // Call the polymorphic method
        }
    }
}
Dans la méthode principale , montrez que les lignes
Dancer breakdancer = new Breakdancer("Jay", 19);
Dancer electricBoogieDancer = new ElectricBoogieDancer("Marcia", 20);
déclarer une variable d'une superclasse et lui affecter un objet qui est une instance de l'un de ses descendants. On vous demandera très probablement pourquoi le compilateur ne se retourne pas face à l'incohérence des types déclarés à gauche et à droite de l'opérateur d'affectation - après tout, Java est fortement typé. Expliquez qu'une conversion de type étendue est à l'œuvre ici — une référence à un objet est traitée comme une référence à sa classe de base. De plus, ayant rencontré une telle construction dans le code, le compilateur effectue la conversion automatiquement et implicitement. L'exemple de code montre que le type déclaré sur le côté gauche de l'opérateur d'affectation ( Dancer ) a plusieurs formes (types), qui sont déclarées sur le côté droit ( Breakdancer , ElectricBoogieDancer). Chaque formulaire peut avoir son propre comportement unique par rapport à la fonctionnalité générale définie dans la superclasse (la méthode de danse ). Autrement dit, une méthode déclarée dans une superclasse peut être implémentée différemment dans ses descendants. Dans ce cas, nous avons affaire à un remplacement de méthode, qui est exactement ce qui crée plusieurs formes (comportements). Cela peut être vu en exécutant le code dans la méthode principale : Sortie du programme : Je suis Fred. J'ai 18 ans. Je danse comme tout le monde. Je suis Jay. J'ai 19 ans. je fais du breakdance ! Je suis Marcia. J'ai 20 ans. Je danse le boogie électrique ! Si nous ne redéfinissons pas la méthode dans les sous-classes, nous n'obtiendrons pas de comportement différent. Par exemple,Cours d'ElectricBoogieDancer , alors la sortie du programme sera ceci : Je suis Fred. J'ai 18 ans. Je danse comme tout le monde. Je suis Jay. J'ai 19 ans. Je danse comme tout le monde. Je suis Marcia. J'ai 20 ans. Je danse comme tout le monde. Et cela signifie qu'il n'est tout simplement pas logique de créer les classes Breakdancer et ElectricBoogieDancer . Où se manifeste précisément le principe du polymorphisme ? Où est un objet utilisé dans le programme sans connaître son type spécifique ? Dans notre exemple, cela se produit lorsque la méthode dance() est appelée sur l' objet Dancer d . En Java, le polymorphisme signifie que le programme n'a pas besoin de savoir si l'objet est unBreakdancer ou ElectricBoogieDancer . L'important est qu'il soit un descendant de la classe Dancer . Et si vous mentionnez des descendants, vous devez noter que l'héritage en Java n'est pas seulement extend , mais aussi implements. Il est maintenant temps de mentionner que Java ne prend pas en charge l'héritage multiple - chaque type peut avoir un parent (superclasse) et un nombre illimité de descendants (sous-classes). En conséquence, les interfaces sont utilisées pour ajouter plusieurs ensembles de fonctions aux classes. Par rapport aux sous-classes (héritage), les interfaces sont moins couplées avec la classe mère. Ils sont très largement utilisés. En Java, une interface est un type référence, le programme peut donc déclarer une variable du type interface. Il est maintenant temps de donner un exemple. Créez une interface :
public interface CanSwim {
    void swim();
}
Pour plus de clarté, nous allons prendre diverses classes non liées et leur faire implémenter l'interface :
public class Human implements CanSwim {
    private String name;
    private int age;

    public Human(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void swim() {
        System.out.println(toString()+" I swim with an inflated tube.");
    }

    @Override
    public String toString() {
        return "I'm " + name + ". I'm " + age + " years old.";
    }

}

public class Fish implements CanSwim {
    private String name;

    public Fish(String name) {
        this.name = name;
    }

    @Override
    public void swim() {
        System.out.println("I'm a fish. My name is " + name + ". I swim by moving my fins.");

    }

public class UBoat implements CanSwim {

    private int speed;

    public UBoat(int speed) {
        this.speed = speed;
    }

    @Override
    public void swim() {
        System.out.println("I'm a submarine that swims through the water by rotating screw propellers. My speed is " + speed + " knots.");
    }
}
méthode principale :
public class Main {

    public static void main(String[] args) {
        CanSwim human = new Human("John", 6);
        CanSwim fish = new Fish("Whale");
        CanSwim boat = new UBoat(25);

        List<swim> swimmers = Arrays.asList(human, fish, boat);
        for (Swim s : swimmers) {
            s.swim();
        }
    }
}
Les résultats appelant une méthode polymorphe définie dans une interface nous montrent les différences de comportement des types qui implémentent cette interface. Dans notre cas, ce sont les différentes chaînes affichées par la méthode swim . Après avoir étudié notre exemple, l'intervieweur peut se demander pourquoi exécuter ce code dans la méthode main
for (Swim s : swimmers) {
            s.swim();
}
provoque l'appel des méthodes prioritaires définies dans nos sous-classes ? Comment l'implémentation souhaitée de la méthode est-elle sélectionnée pendant l'exécution du programme ? Pour répondre à ces questions, vous devez expliquer la liaison tardive (dynamique). La liaison signifie l'établissement d'un mappage entre un appel de méthode et son implémentation de classe spécifique. Essentiellement, le code détermine laquelle des trois méthodes définies dans les classes sera exécutée. Java utilise la liaison tardive par défaut, c'est-à-dire que la liaison se produit au moment de l'exécution et non au moment de la compilation comme c'est le cas avec la liaison anticipée. Cela signifie que lorsque le compilateur compile ce code
for (Swim s : swimmers) {
            s.swim();
}
il ne sait pas quelle classe ( Human , Fish ou Uboat ) contient le code qui sera exécuté lors de la nageméthode est appelée. Ceci n'est déterminé qu'à l'exécution du programme, grâce au mécanisme de liaison dynamique (vérification du type d'un objet à l'exécution et sélection de la bonne implémentation pour ce type). Si on vous demande comment cela est implémenté, vous pouvez répondre que lors du chargement et de l'initialisation des objets, la JVM construit des tables en mémoire et relie les variables avec leurs valeurs et les objets avec leurs méthodes. Ce faisant, si une classe est héritée ou implémente une interface, la première chose à faire est de vérifier la présence de méthodes surchargées. S'il y en a, ils sont liés à ce type. Si ce n'est pas le cas, la recherche d'une méthode correspondante se déplace vers la classe supérieure (le parent) et ainsi de suite jusqu'à la racine dans une hiérarchie à plusieurs niveaux. En ce qui concerne le polymorphisme en POO et son implémentation dans le code, nous notons qu'il est de bonne pratique d'utiliser des classes abstraites et des interfaces pour fournir des définitions abstraites des classes de base. Cette pratique découle du principe d'abstraction - identifier le comportement et les propriétés communs et les mettre dans une classe abstraite, ou identifier uniquement le comportement commun et le mettre dans une interface. La conception et la création d'une hiérarchie d'objets basée sur les interfaces et l'héritage de classe sont nécessaires pour implémenter le polymorphisme. En ce qui concerne le polymorphisme et les innovations en Java, notons qu'à partir de Java 8, lors de la création de classes abstraites et d'interfaces, il est possible d'utiliser le ou identifier uniquement le comportement commun et le mettre dans une interface. La conception et la création d'une hiérarchie d'objets basée sur les interfaces et l'héritage de classe sont nécessaires pour implémenter le polymorphisme. En ce qui concerne le polymorphisme et les innovations en Java, notons qu'à partir de Java 8, lors de la création de classes abstraites et d'interfaces, il est possible d'utiliser le ou identifier uniquement le comportement commun et le mettre dans une interface. La conception et la création d'une hiérarchie d'objets basée sur les interfaces et l'héritage de classe sont nécessaires pour implémenter le polymorphisme. En ce qui concerne le polymorphisme et les innovations en Java, notons qu'à partir de Java 8, lors de la création de classes abstraites et d'interfaces, il est possible d'utiliser lemot-clé default pour écrire une implémentation par défaut pour les méthodes abstraites dans les classes de base. Par exemple:
public interface CanSwim {
    default void swim() {
        System.out.println("I just swim");
    }
}
Parfois, les enquêteurs demandent comment les méthodes des classes de base doivent être déclarées afin que le principe du polymorphisme ne soit pas violé. La réponse est simple : ces méthodes ne doivent pas être static , private ni final . Private rend une méthode disponible uniquement dans une classe, vous ne pourrez donc pas la remplacer dans une sous-classe. Static associe une méthode à la classe plutôt qu'à n'importe quel objet, de sorte que la méthode de la superclasse sera toujours appelée. Et final rend une méthode immuable et cachée des sous-classes.

Que nous donne le polymorphisme ?

On vous demandera aussi très probablement comment le polymorphisme nous profite. Vous pouvez y répondre brièvement sans vous enliser dans les détails épineux :
  1. Il permet de remplacer les implémentations de classe. Les tests sont construits dessus.
  2. Cela facilite l'extensibilité, ce qui facilite grandement la création d'une base sur laquelle on pourra s'appuyer à l'avenir. L'ajout de nouveaux types basés sur ceux existants est le moyen le plus courant d'étendre les fonctionnalités des programmes POO.
  3. Il vous permet de combiner des objets qui partagent un type ou un comportement commun dans une collection ou un tableau et de les gérer de manière uniforme (comme dans nos exemples, où nous avons forcé tout le monde à danser() ou nager() :)
  4. Flexibilité dans la création de nouveaux types : vous pouvez opter pour l'implémentation d'une méthode par le parent ou la remplacer dans une sous-classe.

Quelques mots d'adieu

Le polymorphisme est un sujet très important et étendu. C'est le sujet de près de la moitié de cet article sur la POO en Java et forme une bonne partie de la base du langage. Vous ne pourrez pas éviter de définir ce principe dans une interview. Si vous ne le savez pas ou ne le comprenez pas, l'entretien prendra probablement fin. Alors ne soyez pas un fainéant - évaluez vos connaissances avant l'entretien et rafraîchissez-les si nécessaire.
Commentaires
  • Populaires
  • Nouveau
  • Anciennes
Tu dois être connecté(e) pour laisser un commentaire
Cette page ne comporte pas encore de commentaires