JavaRush /Blog Java /Random-FR /Polymorphisme en Java

Polymorphisme en Java

Publié dans le groupe Random-FR
Les questions sur la POO font partie intégrante d'un entretien technique pour le poste de développeur Java dans une entreprise informatique. Dans cet article, nous parlerons de l'un des principes de la POO : le polymorphisme. Nous nous concentrerons sur les aspects qui sont souvent évoqués lors des entretiens et fournirons également de petits exemples pour plus de clarté.

Qu'est-ce que le polymorphisme ?

Le polymorphisme est la capacité d'un programme à utiliser à l'identique des objets avec la même interface sans information sur le type spécifique de cet objet. Si vous répondez ainsi à la question de savoir ce qu'est le polymorphisme, il vous sera probablement demandé d'expliquer ce que vous voulez dire. Encore une fois, sans poser un tas de questions supplémentaires, mettez tout en ordre pour l'intervieweur.

Polymorphisme en Java lors d'un entretien - 1
Nous pouvons commencer par le fait que l'approche POO implique la construction d'un programme Java basé sur l'interaction d'objets basés sur des classes. Les cours sont des dessins pré-écrits (modèles) selon lesquels les objets du programme seront créés. De plus, une classe a toujours un type spécifique qui, avec un bon style de programmation, « indique » son objectif par son nom. De plus, on peut noter que Java étant un langage fortement typé, le code du programme doit toujours indiquer le type de l'objet lors de la déclaration des variables. Ajoutez à cela que le typage strict augmente la sécurité du code et la fiabilité du programme et permet d'éviter les erreurs d'incompatibilité de type (par exemple, une tentative de division d'une chaîne par un nombre) au stade de la compilation. Naturellement, le compilateur doit « connaître » 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. Veuillez noter à l'intervieweur que lorsque nous travaillons avec du code de programme, nous pouvons utiliser non seulement les objets du type que nous avons attribué lors de la déclaration, mais également ses descendants. C'est un point important : nous pouvons traiter plusieurs types comme s'ils n'en formaient qu'un seul (à condition que ces types soient dérivés d'un type de base). Cela signifie également qu'après avoir déclaré une variable de type superclasse, nous pouvons lui attribuer la valeur d'un de ses descendants. L'intervieweur appréciera si vous donnez un exemple. Sélectionnez un objet qui peut être commun (base) pour un groupe d'objets et héritez de quelques classes. 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 "Я " + name + ", to me " + age + " years. " ;
    }
}
Dans les descendants, remplacez la méthode de la classe de base :
public class ElectricBoogieDancer extends Dancer {
    public ElectricBoogieDancer(String name, int age) {
        super(name, age);
    }
// overriding the base class method
    @Override
    public void dance() {
        System.out.println( toString() + "I dance electric boogie!");
    }
}

public class BreakDankDancer extends Dancer{

    public BreakDankDancer(String name, int age) {
        super(name, age);
    }
// overriding the base class method
    @Override
    public void dance(){
        System.out.println(toString() + "I'm breakdancing!");
    }
}
Un exemple de polymorphisme en Java et d'utilisation d'objets dans un programme :
public class Main {

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

        Dancer breakDanceDancer = new BreakDankDancer("Alexei", 19);// upcast to base type
        Dancer electricBoogieDancer = new ElectricBoogieDancer("Igor", 20); // upcast to base type

        List<Dancer> discotheque = Arrays.asList(dancer, breakDanceDancer, electricBoogieDancer);
        for (Dancer d : discotheque) {
            d.dance();// polymorphic method call
        }
    }
}
mainMontrez dans le code de la méthode ce qu'il y a dans les lignes :
Dancer breakDanceDancer = new BreakDankDancer("Alexei", 19);
Dancer electricBoogieDancer = new ElectricBoogieDancer("Igor", 20);
Nous avons déclaré une variable de type superclasse et lui avons attribué la valeur de l'un des descendants. Très probablement, on vous demandera pourquoi le compilateur ne se plaindra pas de l'inadéquation entre les types déclarés à gauche et à droite du signe d'affectation, puisque Java a un typage strict. Expliquez que la conversion de type ascendante fonctionne ici : une référence à un objet est interprétée comme une référence à la classe de base. De plus, le compilateur, ayant rencontré une telle construction dans le code, le fait automatiquement et implicitement. Sur la base de l'exemple de code, on peut montrer que le type de classe déclaré à gauche du signe d'affectation Dancera plusieurs formes (types) déclarées à droite BreakDankDancer, ElectricBoogieDancer. Chacune des formes peut avoir son propre comportement unique pour les fonctionnalités communes définies dans la méthode superclasse dance. 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, et c'est exactement ce qui crée une variété de formes (comportements). Vous pouvez le voir en exécutant le code de la méthode principale pour l'exécution : Sortie du programme Je m'appelle Anton, j'ai 18 ans. Je danse comme tout le monde. Je m'appelle Alexey, j'ai 19 ans. Je fais du breakdance ! Je m'appelle Igor, j'ai 20 ans. Je danse le boogie électrique ! Si nous n'utilisons pas la substitution dans les descendants, nous n'obtiendrons pas de comportement différent. BreakDankDancerPar exemple, si nous ElectricBoogieDancercommentons la méthode pour nos cours dance, le résultat du programme ressemblera à ceci : Je m'appelle Anton, j'ai 18 ans. Je danse comme tout le monde. Je m'appelle Alexey, j'ai 19 ans. Je danse comme tout le monde. Je m'appelle Igor, j'ai 20 ans. Je danse comme tout le monde. et cela signifie qu'il ne sert tout simplement à rien de créer de nouvelles BreakDankDancerclasses ElectricBoogieDancer. Quel est exactement le principe du polymorphisme Java ? Où est-il caché pour utiliser un objet dans un programme sans connaître son type spécifique ? Dans notre exemple, il s'agit d'un appel de méthode d.dance()sur un objet dde type Dancer. Le polymorphisme Java signifie que le programme n'a pas besoin de savoir de quel type sera l'objet BreakDankDancerou l'objet ElectricBoogieDancer. L'essentiel est qu'il s'agisse d'un descendant de la classe Dancer. Et si nous parlons de descendants, il convient de noter que l'héritage en Java n'est pas seulement extends, mais aussi implements. Il est maintenant temps de se rappeler 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). Par conséquent, les interfaces sont utilisées pour ajouter plusieurs fonctionnalités aux classes. Les interfaces réduisent le couplage d'objets à un parent par rapport à l'héritage et sont très largement utilisées. En Java, une interface est un type référence, un programme peut donc déclarer le type comme étant une variable du type interface. C'est le bon moment pour donner un exemple. Créons l'interface :
public interface Swim {
    void swim();
}
Pour plus de clarté, prenons des objets différents et sans rapport et implémentons-y une interface :
public class Human implements Swim {
    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 inflatable ring.");
    }

    @Override
    public String toString() {
        return "Я " + name + ", to me " + age + " years. ";
    }

}

public class Fish implements Swim{
    private String name;

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

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

    }

public class UBoat implements Swim {

    private int speed;

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

    @Override
    public void swim() {
        System.out.println("The submarine is sailing, rotating the propellers, at a speed" + speed + " knots.");
    }
}
Méthode main:
public class Main {

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

        List<Swim> swimmers = Arrays.asList(human, fish, boat);
        for (Swim s : swimmers) {
            s.swim();
        }
    }
}
Le résultat de l'exécution d'une méthode polymorphe définie dans une interface nous permet de voir des différences dans le comportement des types qui implémentent cette interface. Ils consistent en différents résultats d'exécution de la méthode swim. Après avoir étudié notre exemple, l'intervieweur peut se demander pourquoi, lors de l'exécution du code depuismain
for (Swim s : swimmers) {
            s.swim();
}
Les méthodes définies dans ces classes sont-elles appelées pour nos objets ? Comment sélectionner l’implémentation souhaitée d’une méthode lors de l’exécution d’un programme ? Pour répondre à ces questions, nous devons parler de liaison tardive (dynamique). Par liaison, nous entendons établir une connexion entre un appel de méthode et son implémentation spécifique dans les classes. Essentiellement, le code détermine laquelle des trois méthodes définies dans les classes sera exécutée. Java utilise par défaut une liaison tardive (au moment de l'exécution plutôt qu'au moment de la compilation, comme c'est le cas avec la liaison anticipée). Cela signifie que lors de la compilation du code
for (Swim s : swimmers) {
            s.swim();
}
le compilateur ne sait pas encore de quelle classe provient le code Human, Fishni s'il Uboatsera exécuté dans le swim. Ceci ne sera déterminé que lors de l'exécution du programme grâce au mécanisme de répartition dynamique - vérifiant le type de l'objet lors de l'exécution du programme et sélectionnant l'implémentation souhaitée de la méthode 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 y associe les variables à leurs valeurs et les objets à leurs méthodes. De plus, si un objet est hérité ou implémente une interface, la présence de méthodes surchargées dans sa classe est d'abord vérifiée. S'il y en a, ils sont liés à ce type, sinon on recherche une méthode définie dans la classe un niveau supérieur (dans le parent) et ainsi de suite jusqu'à la racine dans une hiérarchie multi-niveaux. Parlant du polymorphisme en POO et de son implémentation dans le code de programme, nous notons qu'il est de bonne pratique d'utiliser des descriptions abstraites pour définir des classes de base en utilisant des classes abstraites ainsi que des interfaces. Cette pratique est basée sur l'utilisation de l'abstraction - isoler les comportements et propriétés communs et les enfermer dans une classe abstraite, ou isoler uniquement le comportement commun - auquel cas nous créons une interface. Construire et concevoir une hiérarchie d'objets basée sur des interfaces et l'héritage de classes est une condition préalable au respect du principe du polymorphisme POO. Concernant la question du polymorphisme et des innovations en Java, on peut mentionner que lors de la création de classes et d'interfaces abstraites, à partir de Java 8, il est possible d'écrire une implémentation par défaut de méthodes abstraites dans les classes de base en utilisant le mot-clé default. Par exemple:
public interface Swim {
    default void swim() {
        System.out.println("Just floating");
    }
}
Parfois, ils peuvent poser des questions sur les conditions requises pour déclarer des méthodes dans les classes de base afin que le principe du polymorphisme ne soit pas violé. Tout est simple ici : ces méthodes ne doivent pas être static , private et final . Private rend la méthode disponible uniquement dans la classe et vous ne pouvez pas la remplacer dans le descendant. Static fait de la méthode la propriété de la classe, pas de l'objet, donc la méthode superclasse sera toujours appelée. Final rendra la méthode immuable et cachée à ses héritiers.

Que nous apporte le polymorphisme en Java ?

La question de savoir ce que nous apporte l’utilisation du polymorphisme se posera très probablement aussi. Ici, vous pouvez répondre brièvement, sans trop entrer dans les détails :
  1. Vous permet de remplacer les implémentations d'objets. C’est sur cela que sont basés les tests.
  2. Offre une extensibilité du programme - il devient beaucoup plus facile de créer une base pour 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 écrits en style POO.
  3. Vous permet de combiner des objets avec un type ou un comportement commun en une seule collection ou un seul tableau et de les manipuler uniformément (comme dans nos exemples, faire danser tout le monde - une méthode danceou nager - une méthode swim).
  4. Flexibilité lors de la création de nouveaux types : vous pouvez choisir d'implémenter une méthode provenant d'un parent ou de la remplacer chez un enfant.

Mots d'adieu pour le voyage

Le principe du polymorphisme est un sujet très important et vaste. Il couvre près de la moitié de la POO de Java et une bonne partie des principes fondamentaux du langage. Vous ne pourrez pas vous permettre de définir ce principe en entretien. L’ignorance ou une mauvaise compréhension de ce point mettra très probablement fin à l’entretien. Ne soyez donc pas paresseux pour vérifier vos connaissances avant le test et les rafraîchir si nécessaire.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION