116 votes

Liaison statique vs dynamique en Java

Je suis actuellement en train de faire un devoir pour l'un de mes cours, et dedans, je dois donner des exemples, en utilisant la syntaxe Java, de liaison statique et de liaison dynamique.

Je comprends le concept de base, selon lequel la liaison statique se produit au moment de la compilation et la liaison dynamique se produit à l'exécution, mais je n'arrive pas vraiment à comprendre comment elles fonctionnent spécifiquement.

J'ai trouvé un exemple de liaison statique en ligne qui donne cet exemple :

public static void callEat(Animal animal) {
    System.out.println("L'animal mange");
}

public static void callEat(Dog dog) {
    System.out.println("Le chien mange");
}

public static void main(String args[])
{
    Animal a = new Dog();
    callEat(a);
}

Et que cela afficherait "l'animal mange" parce que l'appel à callEat utilise une liaison statique, mais je suis incertain quant à pourquoi cela est considéré comme une liaison statique.

Jusqu'à présent, aucun des sources que j'ai consultées n'a réussi à m'expliquer de manière claire.

133voto

Maulik Patel Points 2552

De l'article de blog de Javarevisited:

Voici quelques différences importantes entre la liaison statique et dynamique :

  1. La liaison statique en Java se produit pendant la compilation tandis que la liaison dynamique se produit pendant l'exécution.
  2. Les méthodes et variables private, final et static utilisent la liaison statique et sont liées par le compilateur tandis que les méthodes virtuelles sont liées pendant l'exécution en fonction de l'objet d'exécution.
  3. La liaison statique utilise les informations de Type (class en Java) pour la liaison tandis que la liaison dynamique utilise l'objet pour résoudre la liaison.
  4. Les méthodes surchargées sont liées en utilisant la liaison statique tandis que les méthodes remplacées sont liées en utilisant la liaison dynamique pendant l'exécution.

Voici un exemple qui vous aidera à comprendre à la fois la liaison statique et dynamique en Java.

Exemple de liaison statique en Java

public class StaticBindingTest {  
    public static void main(String args[]) {
        Collection c = new HashSet();
        StaticBindingTest et = new StaticBindingTest();
        et.sort(c);
    }
    // méthode surchargée qui prend un argument de type Collection
    public Collection sort(Collection c) {
        System.out.println("À l'intérieur de la méthode de tri de Collection");
        return c;
    }
    // une autre méthode surchargée qui prend un argument de type HashSet qui est une sous-classe
    public Collection sort(HashSet hs) {
        System.out.println("À l'intérieur de la méthode de tri de HashSet");
        return hs;
    }
}

Résultat: À l'intérieur de la méthode de tri de Collection

Exemple de liaison dynamique en Java

public class DynamicBindingTest {   
    public static void main(String args[]) {
        Vehicle vehicle = new Car(); // ici le Type est vehicle mais l'objet sera Car
        vehicle.start(); // start() de Car est appelé car start() est une méthode remplacée
    }
}

class Vehicle {
    public void start() {
        System.out.println("À l'intérieur de la méthode start de Vehicle");
    }
}

class Car extends Vehicle {
    @Override
    public void start() {
        System.out.println("À l'intérieur de la méthode start de Car");
    }
}

Résultat: À l'intérieur de la méthode start de Car

23voto

user3593084 Points 42

Connecter un appel de méthode au corps de la méthode est connu sous le nom de Liaison. Comme l'a dit Maulik "La liaison statique utilise les informations Type (Classe en Java) pour la liaison tandis que la liaison dynamique utilise l'objet pour résoudre la liaison." Donc ce code :

public class Animal {
    void eat() {
        System.out.println("l'animal mange...");
    }
}

class Dog extends Animal {

    public static void main(String args[]) {
        Animal a = new Dog();
        a.eat(); // imprime >> le chien mange...
    }

    @Override
    void eat() {
        System.out.println("le chien mange...");
    }
}

Produira le résultat : le chien mange... parce qu'il utilise la référence de l'objet pour trouver quelle méthode utiliser. Si nous modifions le code ci-dessus pour cela :

class Animal {
    static void eat() {
        System.out.println("l'animal mange...");
    }
}

class Dog extends Animal {

    public static void main(String args[]) {

        Animal a = new Dog();
        a.eat(); // imprime >> l'animal mange...

    }

    static void eat() {
        System.out.println("le chien mange...");
    }
}

Cela produira : l'animal mange... parce que c'est une méthode statique, donc cela utilise le Type (dans ce cas Animal) pour résoudre quelle méthode statique appeler. En plus des méthodes statiques, les méthodes privées et finales utilisent la même approche.

11voto

Naresh Joshi Points 1531

Eh bien pour comprendre comment la liaison statique et dynamique fonctionne réellement ? ou comment elles sont identifiées par le compilateur et la JVM ?

Prenons l'exemple ci-dessous où Mammal est une classe parent qui a une méthode speak() et la classe Human étend la classe Mammal, substitue la méthode speak() et la surcharge ensuite avec speak(String language).

public class OverridingInternalExample {

    private static class Mammal {
        public void speak() { System.out.println("ohlllalalalalalaoaoaoa"); }
    }

    private static class Human extends Mammal {

        @Override
        public void speak() { System.out.println("Hello"); }

        // Valid overload of speak
        public void speak(String language) {
            if (language.equals("Hindi")) System.out.println("Namaste");
            else System.out.println("Hello");
        }

        @Override
        public String toString() { return "Classe Humaine"; }

    }

    //  Code ci-dessous contient la sortie et le bytecode des appels de méthode
    public static void main(String[] args) {
        Mammal anyMammal = new Mammal();
        anyMammal.speak();  // Sortie - ohlllalalalalalaoaoaoa
        // 10: invokevirtual #4 // Méthode org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V

        Mammal humanMammal = new Human();
        humanMammal.speak(); // Sortie - Hello
        // 23: invokevirtual #4 // Méthode org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V

        Human human = new Human();
        human.speak(); // Sortie - Hello
        // 36: invokevirtual #7 // Méthode org/programming/mitra/exercises/OverridingInternalExample$Human.speak:()V

        human.speak("Hindi"); // Sortie - Namaste
        // 42: invokevirtual #9 // Méthode org/programming/mitra/exercises/OverridingInternalExample$Human.speak:(Ljava/lang/String;)V
    }
}

Lorsque nous compilons le code ci-dessus et essayons de regarder le bytecode en utilisant javap -verbose OverridingInternalExample, nous pouvons voir que le compilateur génère une table constante où il attribue des codes entiers à chaque appel de méthode et du bytecode pour le programme que j'ai extrait et inclus dans le programme lui-même (voir les commentaires après chaque appel de méthode)

Programme Bytecode

En regardant le code ci-dessus, nous pouvons voir que les bytecodes de humanMammal.speak(), human.speak() et human.speak("Hindi") sont totalement différents (invokevirtual #4, invokevirtual #7, invokevirtual #9) parce que le compilateur est capable de les différencier en fonction de la liste d'arguments et de la référence de classe. Parce que tout cela est résolu au moment de la compilation statiquement c'est pourquoi la surcharge de méthode est connue sous le nom de Polymorphisme Statique ou Liaison Statique.

Mais le bytecode pour anyMammal.speak() et humanMammal.speak() est le même (invokevirtual #4) car selon le compilateur les deux méthodes sont appelées sur la référence de Mammal.

Alors maintenant la question se pose si les deux appels de méthode ont le même bytecode alors comment la JVM sait quelle méthode appeler ?

Eh bien, la réponse est cachée dans le bytecode lui-même et c'est l'instruction invokevirtual. La JVM utilise l'instruction invokevirtual pour appeler l'équivalent en Java des méthodes virtuelles en C++. En C++ si nous voulons substituer une méthode dans une autre classe, nous devons la déclarer comme virtuelle, mais en Java, toutes les méthodes sont virtuelles par défaut car nous pouvons substituer chaque méthode dans la classe enfant (sauf les méthodes privées, finales et statiques).

En Java, chaque variable de référence contient deux pointeurs cachés

  1. Un pointeur vers une table qui contient à nouveau des méthodes de l'objet et un pointeur vers l'objet Classe. par ex. [speak(), speak(String) Classe]
  2. Un pointeur vers la mémoire allouée sur le tas pour les données de cet objet par ex. valeurs des variables d'instance.

Ainsi, toutes les références d'objets détiennent indirectement une référence à une table qui contient toutes les références de méthodes de cet objet. Java a emprunté ce concept à C++ et cette table est connue sous le nom de table virtuelle (vtable).

Une vtable est une structure de type tableau qui contient les noms de méthodes virtuelles et leurs références sur des indices de tableau. JVM crée seulement une vtable par classe lorsqu'elle charge la classe en mémoire.

Donc chaque fois que la JVM rencontre avec l'instruction invokevirtual, elle vérifie la vtable de cette classe pour la référence de méthode et appelle la méthode spécifique qui dans notre cas est la méthode d'un objet et non la référence.

Parce que tout cela est résolu seulement au moment de l'exécution et à l'exécution la JVM sait quelle méthode appeler, c'est pourquoi le remplacement de méthode est connu sous le nom de Polymorphisme Dynamique ou simplement Polymorphisme ou Liaison Dynamique.

Vous pouvez en lire plus de détails sur mon article Comment la JVM gère la surcharge et le remplacement des méthodes en interne.

4voto

user2378479 Points 49

Le compilateur sait seulement que le type de "a" est Animal; ceci se produit au moment de la compilation, c'est pourquoi on parle de liaison statique (surcharge de méthode). Mais s'il s'agit d'une liaison dynamique, alors il appellerait la méthode de la classe Dog. Voici un exemple de liaison dynamique.

public class DynamicBindingTest {

    public static void main(String args[]) {
        Animal a= new Dog(); //ici le Type est Animal mais l'objet sera de type Dog
        a.eat();       //l'appel à eat() va appeler la méthode de Dog car elle est redéfinie
    }
}

class Animal {

    public void eat() {
        System.out.println("À l'intérieur de la méthode eat d'Animal");
    }
}

class Dog extends Animal {

    @Override
    public void eat() {
        System.out.println("À l'intérieur de la méthode eat de Dog");
    }
}

Sortie: À l'intérieur de la méthode eat de Dog

2voto

hexpheus Points 392

Il existe trois principales différences entre la liaison statique et la liaison dynamique lors de la conception des compilateurs et de la manière dont les variables et les procédures sont transférées à l'environnement d'exécution. Ces différences sont les suivantes :

Liaison statique : Dans la liaison statique, les trois problèmes suivants sont discutés :

  • Définition d'une procédure

  • Déclaration d'un nom (variable, etc.)

  • Portée de la déclaration

Liaison dynamique : Trois problèmes se posent dans la liaison dynamique, à savoir :

  • Activation d'une procédure

  • Liaison d'un nom

  • Durée de vie d'une liaison

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X