Recherche…


Introduction

Le polymorphisme est l'un des principaux concepts de programmation orientée objet (OOP). Le mot polymorphisme était dérivé des mots grecs "poly" et "morphs". Poly signifie "beaucoup" et morphs signifie "formes" (nombreuses formes).

Il y a deux façons d'effectuer un polymorphisme. Surcharge de méthode et remplacement de méthode .

Remarques

Interfaces sont un autre moyen d'obtenir un polymorphisme en Java, hormis l'héritage basé sur les classes. Les interfaces définissent une liste de méthodes formant l'API du programme. Les classes doivent implement une interface en remplaçant toutes ses méthodes.

Surcharge de méthode

La surcharge de méthode , également appelée surcharge de fonction , est la capacité d'une classe à avoir plusieurs méthodes portant le même nom, à condition qu'elles diffèrent en nombre ou en type d'arguments.

Le compilateur vérifie la signature de la méthode pour la surcharge de la méthode.

La signature de la méthode consiste en trois choses -

  1. Nom de la méthode
  2. Nombre de paramètres
  3. Types de paramètres

Si ces trois méthodes sont identiques pour deux méthodes d'une classe, alors le compilateur génère une erreur de méthode en double .

Ce type de polymorphisme est appelé polymorphisme statique ou temps de compilation car la méthode appropriée à appeler est décidée par le compilateur pendant la compilation à partir de la liste des arguments.

class Polymorph {

    public int add(int a, int b){
        return a + b;
    }
    
    public int add(int a, int b, int c){
        return a + b + c;
    }

    public float add(float a, float b){
        return a + b;
    }

    public static void main(String... args){
        Polymorph poly = new Polymorph();
        int a = 1, b = 2, c = 3;
        float d = 1.5, e = 2.5;

        System.out.println(poly.add(a, b));
        System.out.println(poly.add(a, b, c));
        System.out.println(poly.add(d, e));
    }

}

Cela se traduira par:

2
6
4.000000

Les méthodes surchargées peuvent être statiques ou non statiques. Cela n'affecte pas non plus la surcharge de la méthode.

public class Polymorph {

    private static void methodOverloaded()
    {
        //No argument, private static method
    }
 
    private int methodOverloaded(int i)
    {
        //One argument private non-static method
        return i;
    }
 
    static int methodOverloaded(double d)
    {
        //static Method
        return 0;
    }
 
    public void methodOverloaded(int i, double d)
    {
        //Public non-static Method
    }
}

De plus, si vous modifiez le type de méthode de retour, nous ne pouvons pas l'obtenir en tant que surcharge de méthode.

public class Polymorph {  

void methodOverloaded(){
    //No argument and No return type
}

int methodOverloaded(){
    //No argument and int return type 
    return 0;
}

Dérogation de méthode

La substitution de méthode est la capacité des sous-types à redéfinir (outrepasser) le comportement de leurs sur-types.

En Java, cela se traduit par des sous-classes remplaçant les méthodes définies dans la super classe. En Java, toutes les variables non primitives sont en fait des references , qui s'apparentent à des pointeurs vers l'emplacement de l'objet réel en mémoire. Les references ont un seul type, qui est le type avec lequel elles ont été déclarées. Cependant, ils peuvent pointer vers un objet de leur type déclaré ou de l'un de ses sous-types.

Lorsqu'une méthode est appelée sur une reference , la méthode correspondante de l'objet réel pointé est appelée .

class SuperType {
    public void sayHello(){
        System.out.println("Hello from SuperType");
    }

    public void sayBye(){
        System.out.println("Bye from SuperType");
    }
}

class SubType extends SuperType {
    // override the superclass method
    public void sayHello(){
        System.out.println("Hello from SubType");
    }
}

class Test {
    public static void main(String... args){
        SuperType superType = new SuperType();
        superType.sayHello(); // -> Hello from SuperType

        // make the reference point to an object of the subclass
        superType = new SubType();
        // behaviour is governed by the object, not by the reference
        superType.sayHello(); // -> Hello from SubType

        // non-overridden method is simply inherited
        superType.sayBye(); // -> Bye from SuperType
    }
}

Règles à garder à l'esprit

Pour remplacer une méthode dans la sous-classe, la méthode de substitution (c.-à-d. Celle de la sous-classe) DOIT AVOIR :

  • même nom
  • même type de retour en cas de primitives (une sous-classe est autorisée pour les classes, c'est ce qu'on appelle les types de retour covariants).
  • même type et ordre de paramètres
  • il ne peut lancer que les exceptions déclarées dans la clause throws de la méthode de la superclasse ou les exceptions qui sont des sous-classes des exceptions déclarées. Il peut également choisir de ne lancer aucune exception. Les noms des types de paramètres n'ont pas d'importance. Par exemple, void methodX (int i) est identique à void methodX (int k)
  • Nous ne pouvons pas remplacer les méthodes finales ou statiques. Seule chose que nous ne pouvons faire que changer de corps de méthode.

Ajout de comportement en ajoutant des classes sans toucher au code existant

import java.util.ArrayList;
import java.util.List;

import static java.lang.System.out;

public class PolymorphismDemo {

    public static void main(String[] args) {
        List<FlyingMachine> machines = new ArrayList<FlyingMachine>();
        machines.add(new FlyingMachine());
        machines.add(new Jet());
        machines.add(new Helicopter());
        machines.add(new Jet());

        new MakeThingsFly().letTheMachinesFly(machines);
    }
}

class MakeThingsFly {
    public void letTheMachinesFly(List<FlyingMachine> flyingMachines) {
        for (FlyingMachine flyingMachine : flyingMachines) {
            flyingMachine.fly();
        }
    }
}

class FlyingMachine {
    public void fly() {
        out.println("No implementation");
    }
}

class Jet extends FlyingMachine {
    @Override
    public void fly() {
        out.println("Start, taxi, fly");
    }

    public void bombardment() {
        out.println("Fire missile");
    }
}

class Helicopter extends FlyingMachine {
    @Override
    public void fly() {
        out.println("Start vertically, hover, fly");
    }
}

Explication

a) La classe MakeThingsFly peut fonctionner avec tout ce qui est de type FlyingMachine .

b) La méthode letTheMachinesFly fonctionne également sans aucun changement (!) lorsque vous ajoutez une nouvelle classe, par exemple PropellerPlane :

public void letTheMachinesFly(List<FlyingMachine> flyingMachines) {
        for (FlyingMachine flyingMachine : flyingMachines) {
            flyingMachine.fly();
        }
    }
}

C'est le pouvoir du polymorphisme. Vous pouvez implémenter le principe d' ouverture-fermeture avec celui - ci.

Fonctions virtuelles

Les méthodes virtuelles sont des méthodes en Java non statiques et sans le mot-clé Final en tête. Toutes les méthodes par défaut sont virtuelles en Java. Les méthodes virtuelles jouent un rôle important dans le polymorphisme, car les classes enfants de Java peuvent remplacer les méthodes de leurs classes parentes si la fonction remplacée est non statique et possède la même signature de méthode.

Il existe cependant des méthodes qui ne sont pas virtuelles. Par exemple, si la méthode est déclarée privée ou avec le mot clé final, la méthode n'est pas virtuelle.

Considérez l'exemple modifié suivant d'héritage avec les méthodes virtuelles de cette publication StackOverflow Comment les fonctions virtuelles fonctionnent-elles en C # et en Java? :

public class A{
    public void hello(){
        System.out.println("Hello");
    }
    
    public void boo(){
        System.out.println("Say boo");

    }
}

public class B extends A{
     public void hello(){
        System.out.println("No");
     }
    
    public void boo(){
        System.out.println("Say haha");

    }
}

Si nous invoquons la classe B et appelons hello () et boo (), nous obtiendrions "No" et "Say haha" comme résultat car B remplace les mêmes méthodes de A. Même si l'exemple ci-dessus est presque identique à la méthode outrepassant, il est important de comprendre que les méthodes de la classe A sont toutes, par défaut, virtuelles.

De plus, nous pouvons implémenter des méthodes virtuelles en utilisant le mot-clé abstract. Les méthodes déclarées avec le mot-clé "abstract" n'ont pas de définition de méthode, ce qui signifie que le corps de la méthode n'est pas encore implémenté. Prenons à nouveau l'exemple ci-dessus, sauf que la méthode boo () est déclarée abstraite:

public class A{
   public void hello(){
        System.out.println("Hello");
    }
    
    abstract void boo();
}

public class B extends A{
     public void hello(){
        System.out.println("No");
     }
    
    public void boo(){
        System.out.println("Say haha");

    }
}

Si nous invoquons boo () à partir de B, la sortie sera toujours "Say haha" car B hérite de la méthode abstraite boo () et rend la sortie boo () "Say haha".

Sources utilisées et lectures supplémentaires:

Comment fonctionnent les fonctions virtuelles en C # et en Java?

Découvrez cette excellente réponse qui fournit des informations beaucoup plus complètes sur les fonctions virtuelles:

Pouvez-vous écrire des fonctions / méthodes virtuelles en Java?

Polymorphisme et différents types de dépassement

De tutoriel Java

La définition du dictionnaire du polymorphisme fait référence à un principe en biologie dans lequel un organisme ou une espèce peut avoir plusieurs formes ou stades différents. Ce principe peut également être appliqué à la programmation orientée objet et à des langages tels que le langage Java. Les sous-classes d'une classe peuvent définir leurs propres comportements uniques tout en partageant certaines des mêmes fonctionnalités de la classe parente.

Regardez cet exemple pour comprendre différents types de dépassement.

  1. La classe de base ne fournit aucune implémentation et la sous-classe doit remplacer la méthode complète - (résumé)
  2. La classe de base fournit une implémentation par défaut et la sous-classe peut changer le comportement
  3. La sous-classe ajoute l'extension à l'implémentation de la classe de base en appelant super.methodName() tant que première instruction
  4. La classe de base définit la structure de l'algorithme (méthode Template) et la sous-classe remplacera une partie de l'algorithme

extrait de code:

import java.util.HashMap;

abstract class Game implements Runnable{

    protected boolean runGame = true;
    protected Player player1 = null;
    protected Player player2 = null;
    protected Player currentPlayer = null;
    
    public Game(){
        player1 = new Player("Player 1");
        player2 = new Player("Player 2");
        currentPlayer = player1;
        initializeGame();
    }

    /* Type 1: Let subclass define own implementation. Base class defines abstract method to force
        sub-classes to define implementation    
    */
    
    protected abstract void initializeGame();
    
    /* Type 2: Sub-class can change the behaviour. If not, base class behaviour is applicable */
    protected void logTimeBetweenMoves(Player player){
        System.out.println("Base class: Move Duration: player.PlayerActTime - player.MoveShownTime");
    }
    
    /* Type 3: Base class provides implementation. Sub-class can enhance base class implementation by calling
        super.methodName() in first line of the child class method and specific implementation later */
    protected void logGameStatistics(){
        System.out.println("Base class: logGameStatistics:");
    }
    /* Type 4: Template method: Structure of base class can't be changed but sub-class can some part of behaviour */
    protected void runGame() throws Exception{
        System.out.println("Base class: Defining the flow for Game:");    
        while (runGame) {
            /*
            1. Set current player
            2. Get Player Move
            */
            validatePlayerMove(currentPlayer);    
            logTimeBetweenMoves(currentPlayer);
            Thread.sleep(500);
            setNextPlayer();
        }
        logGameStatistics();
    }
    /* sub-part of the template method, which define child class behaviour */
    protected abstract void validatePlayerMove(Player p);
    
    protected void setRunGame(boolean status){
        this.runGame = status;
    }
    public void setCurrentPlayer(Player p){
        this.currentPlayer = p;
    }
    public void setNextPlayer(){
        if (currentPlayer == player1) {
            currentPlayer = player2;
        }else{
            currentPlayer = player1;
        }
    }
    public void run(){
        try{
            runGame();
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}

class Player{
    String name;
    Player(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
}

/* Concrete Game implementation  */
class Chess extends Game{
    public Chess(){
        super();
    }
    public void initializeGame(){
        System.out.println("Child class: Initialized Chess game");
    }
    protected void validatePlayerMove(Player p){
        System.out.println("Child class: Validate Chess move:" + p.getName());
    }
    protected void logGameStatistics(){
        super.logGameStatistics();
        System.out.println("Child class: Add Chess specific logGameStatistics:");
    }
}
class TicTacToe extends Game{
    public TicTacToe(){
        super();
    }
    public void initializeGame(){
        System.out.println("Child class: Initialized TicTacToe game");
    }
    protected void validatePlayerMove(Player p){
        System.out.println("Child class: Validate TicTacToe move:" + p.getName());
    }
}

public class Polymorphism{
    public static void main(String args[]){
        try{
        
            Game game = new Chess();
            Thread t1 = new Thread(game);
            t1.start();
            Thread.sleep(1000);
            game.setRunGame(false);
            Thread.sleep(1000);
                        
            game = new TicTacToe();
            Thread t2 = new Thread(game);
            t2.start();
            Thread.sleep(1000);
            game.setRunGame(false);
        
        }catch(Exception err){
            err.printStackTrace();
        }        
    }
}

sortie:

Child class: Initialized Chess game
Base class: Defining the flow for Game:
Child class: Validate Chess move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate Chess move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:
Child class: Add Chess specific logGameStatistics:

Child class: Initialized TicTacToe game
Base class: Defining the flow for Game:
Child class: Validate TicTacToe move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate TicTacToe move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow