Vs statique. Liaison dynamique en Java


103

Je fais actuellement un devoir pour l'une de mes classes, et dans celui-ci, je dois donner des exemples, en utilisant la syntaxe Java, de liaison statique et dynamique .

Je comprends le concept de base, que la liaison statique se produit au moment de la compilation et la liaison dynamique se produit au moment de l'exécution, mais je ne peux pas comprendre comment elles fonctionnent réellement.

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

public static void callEat(Animal animal) {
    System.out.println("Animal is eating");
}

public static void callEat(Dog dog) {
    System.out.println("Dog is eating");
}

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

Et que cela afficherait "l'animal mange" parce que l'appel à callEatutilise la liaison statique , mais je ne suis pas sûr de savoir pourquoi cela est considéré comme une liaison statique.

Jusqu'à présent, aucune des sources que j'ai vues n'a réussi à expliquer cela d'une manière que je puisse suivre.



1
Notez qu'il existe plusieurs concepts différents appelés «liaison». Dans ce cas particulier, pour ce type de liaison (qui implique un choix entre des méthodes "signatures" similaires mais non identiques) le compilateur (qui prend des décisions "statiques", puisqu'elles ne varient pas à l'exécution) décide que le paramètre est un "Animal" parce que c'est le type (statique) de la variable "a".
Hot Licks

(Il y a des langues où le choix de la signature de méthode spécifique serait laissé jusqu'à l'exécution, et callEat (Dog) serait sélectionné.)
Hot Licks

Réponses:


114

De l' article de blog 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. private, finalEt les staticméthodes et variables utilisent la liaison statique et sont liés par le compilateur alors que les méthodes virtuelles sont liés lors de l' exécution sur la base d'objet d'exécution.
  3. La liaison statique utilise Type( classen Java) des informations pour la liaison tandis que la liaison dynamique utilise un objet pour résoudre la liaison.
  4. Les méthodes surchargées sont liées à l'aide d'une liaison statique tandis que les méthodes remplacées sont liées à l'aide d'une liaison dynamique au moment de 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);
    }
    //overloaded method takes Collection argument
    public Collection sort(Collection c) {
        System.out.println("Inside Collection sort method");
        return c;
    }
    //another overloaded method which takes HashSet argument which is sub class
    public Collection sort(HashSet hs) {
        System.out.println("Inside HashSet sort method");
        return hs;
    }
}

Sortie : méthode de tri à l'intérieur de la collection

Exemple de liaison dynamique en Java

public class DynamicBindingTest {   
    public static void main(String args[]) {
        Vehicle vehicle = new Car(); //here Type is vehicle but object will be Car
        vehicle.start(); //Car's start called because start() is overridden method
    }
}

class Vehicle {
    public void start() {
        System.out.println("Inside start method of Vehicle");
    }
}

class Car extends Vehicle {
    @Override
    public void start() {
        System.out.println("Inside start method of Car");
    }
}

Sortie: méthode de démarrage intérieur de la voiture



11
Je ne comprends toujours pas la différence,
technazi

9
La liaison statique @technazi ne regarde que le type (ce qui se trouve avant l'égal, par exemple Collection c = new HashSet (); il sera donc considéré comme un objet de collection lorsqu'il s'agit en fait d'un hashset). La liaison Dyanmic prend en compte l'objet réel (ce qui est après les égaux pour qu'il reconnaisse réellement son HashSet).
Mark

22

La connexion d'un appel de méthode au corps de la méthode est connue sous le nom de Binding. Comme l'a dit Maulik, «la liaison statique utilise les informations de type (classe en Java) pour la liaison, tandis que la liaison dynamique utilise Object pour résoudre la liaison». Donc ce code:

public class Animal {
    void eat() {
        System.out.println("animal is eating...");
    }
}

class Dog extends Animal {

    public static void main(String args[]) {
        Animal a = new Dog();
        a.eat(); // prints >> dog is eating...
    }

    @Override
    void eat() {
        System.out.println("dog is eating...");
    }
}

Produira le résultat: le chien mange ... car il utilise la référence d'objet pour trouver la méthode à utiliser. Si nous changeons le code ci-dessus en ceci:

class Animal {
    static void eat() {
        System.out.println("animal is eating...");
    }
}

class Dog extends Animal {

    public static void main(String args[]) {

        Animal a = new Dog();
        a.eat(); // prints >> animal is eating...

    }

    static void eat() {
        System.out.println("dog is eating...");
    }
}

Il produira: animal mange ... parce que c'est une méthode statique, il utilise donc Type (dans ce cas Animal) pour déterminer quelle méthode statique appeler. Outre les méthodes statiques, les méthodes privées et finales utilisent la même approche.


1
Pourquoi Java ne peut-il pas en déduire qu'il as'agit en fait d'un Dogau moment de la compilation?
Minh Nghĩa

4

Le compilateur sait seulement que le type de "a" est Animal ; cela se produit au moment de la compilation, à cause de quoi il est appelé liaison statique (surcharge de méthode). Mais s'il s'agit d'une liaison dynamique, il appellerait la Dogméthode de classe. Voici un exemple de liaison dynamique.

public class DynamicBindingTest {

    public static void main(String args[]) {
        Animal a= new Dog(); //here Type is Animal but object will be Dog
        a.eat();       //Dog's eat called because eat() is overridden method
    }
}

class Animal {

    public void eat() {
        System.out.println("Inside eat method of Animal");
    }
}

class Dog extends Animal {

    @Override
    public void eat() {
        System.out.println("Inside eat method of Dog");
    }
}

Sortie: Méthode de manger à l'intérieur du chien


Cela ne lèverait-il pas une erreur de compilation telle que "Impossible de référencer une classe / méthode non statique à partir d'un contexte statique"? Je suis toujours confus avec cela, sachant que le principal est statique. Merci d'avance.
Amnor

3

Eh bien, afin de comprendre comment la liaison statique et dynamique fonctionne réellement la ? ou comment ils sont identifiés par le compilateur et JVM?

Prenons l'exemple ci-dessous où se Mammaltrouve une classe parente qui a une méthode speak()et une Humanclasse étend Mammal, remplace la speak()méthode et la surcharge à nouveau 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 "Human Class"; }

    }

    //  Code below contains the output and bytecode of the method calls
    public static void main(String[] args) {
        Mammal anyMammal = new Mammal();
        anyMammal.speak();  // Output - ohlllalalalalalaoaoaoa
        // 10: invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V

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

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

        human.speak("Hindi"); // Output - Namaste
        // 42: invokevirtual #9 // Method 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 affecte des codes entiers à chaque appel de méthode et code d'octet pour le programme que j'ai extrait et inclus dans le programme lui-même ( voir les commentaires ci-dessous chaque appel de méthode)

Bytecode du programme

En regardant le code ci - dessus , nous pouvons voir que le bytecode 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 faire la différence entre eux en fonction de la liste des arguments et référence de classe. Parce que tout cela est résolu au moment de la compilation de manière statique, c'est pourquoi la surcharge de méthode est connue sous le nom de polymorphisme statique ou statique liaison statique .

Mais bytecode pour anyMammal.speak()et humanMammal.speak()est identique ( invokevirtual #4) car selon le compilateur les deux méthodes sont appeléesMammal référence.

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

Eh bien, la réponse est cachée dans le bytecode lui-même et c'est invokevirtualun jeu d'instructions. JVM utilise leinvokevirtual instruction pour appeler l'équivalent Java des méthodes virtuelles C ++. En C ++, si nous voulons remplacer 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 remplacer toutes les méthodes de la classe enfant (à l'exception des 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 les méthodes de l'objet et un pointeur vers l'objet Class. par exemple [speak (), speak (String) Class object]
  2. Un pointeur vers la mémoire allouée sur le tas pour les données de cet objet, par exemple les valeurs des variables d'instance.

Ainsi, toutes les références d'objet contiennent indirectement une référence à une table qui contient toutes les références de méthode 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 les index de tableau. JVM crée une seule vtable par classe lorsqu'elle charge la classe en mémoire.

Ainsi, chaque fois que JVM rencontre un invokevirtualjeu d'instructions, il vérifie la vtable de cette classe pour la référence de méthode et invoque 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 au moment de l'exécution uniquement et au moment de 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 de polymorphisme ou de liaison dynamique. .

Vous pouvez le lire plus en détail sur mon article Comment la JVM gère-t-elle la surcharge et le remplacement de méthode en interne .


2

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

Liaison statique : dans la liaison statique, trois problèmes suivants sont abordé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 rencontrés dans la liaison dynamique sont les suivants:

  • Activation d'une procédure

  • Liaison d'un nom

  • Durée de vie d'une reliure


1

Avec la méthode statique dans la classe parent et enfant: liaison statique

public class test1 {   
    public static void main(String args[]) {
        parent pc = new child(); 
        pc.start(); 
    }
}

class parent {
    static public void start() {
        System.out.println("Inside start method of parent");
    }
}

class child extends parent {

    static public void start() {
        System.out.println("Inside start method of child");
    }
}

// Output => Inside start method of parent

Liaison dynamique:

public class test1 {   
    public static void main(String args[]) {
        parent pc = new child();
        pc.start(); 
    }
}

class parent {
   public void start() {
        System.out.println("Inside start method of parent");
    }
}

class child extends parent {

   public void start() {
        System.out.println("Inside start method of child");
    }
}

// Output => Inside start method of child

0

Toutes les réponses ici sont correctes mais je veux ajouter quelque chose qui manque. lorsque vous surchargez une méthode statique, il semble que nous la surchargions, mais en réalité ce n'est pas une substitution de méthode. Au lieu de cela, il est appelé masquage de méthode. Les méthodes statiques ne peuvent pas être remplacées en Java.

Regardez l'exemple ci-dessous:

class Animal {
    static void eat() {
        System.out.println("animal is eating...");
    }
}

class Dog extends Animal {

    public static void main(String args[]) {

        Animal a = new Dog();
        a.eat(); // prints >> animal is eating...

    }

    static void eat() {
        System.out.println("dog is eating...");
    }
}

Dans la liaison dynamique, la méthode est appelée en fonction du type de référence et non du type d'objet que la variable de référence contient. Ici, la liaison statique se produit car le masquage de méthode n'est pas un polymorphisme dynamique. Si vous supprimez le mot-clé static devant eat () et en faites une méthode non statique, cela vous montrera un polymorphisme dynamique et non un masquage de méthode.

J'ai trouvé le lien ci-dessous pour soutenir ma réponse: https://youtu.be/tNgZpn7AeP0


0

Dans le cas du type d'objet de liaison statique déterminé au moment de la compilation, tandis que dans le cas du type de liaison dynamique de l'objet est déterminé au moment de l'exécution.



class Dainamic{

    void run2(){
        System.out.println("dainamic_binding");
    }

}


public class StaticDainamicBinding extends Dainamic {

    void run(){
        System.out.println("static_binding");
    }

    @Override
    void run2() {
        super.run2();
    }

    public static void main(String[] args) {
        StaticDainamicBinding st_vs_dai = new StaticDainamicBinding();
        st_vs_dai.run();
        st_vs_dai.run2();
    }

}

-3

Parce que le compilateur connaît la liaison au moment de la compilation. Si vous invoquez une méthode sur une interface, par exemple, le compilateur ne peut pas le savoir et la liaison est résolue au moment de l'exécution car l'objet réel sur lequel une méthode est invoquée pourrait être l'un de plusieurs. Par conséquent, il s'agit du runtime ou de la liaison dynamique.

Votre appel est lié à la classe Animal au moment de la compilation car vous avez spécifié le type. Si vous passiez cette variable dans une autre méthode ailleurs, personne ne saurait (à part vous parce que vous l'avez écrite) de quelle classe il s'agirait. Le seul indice est le type d'animal déclaré.


1
Pas vrai. Le compilateur prendrait exactement la même décision si vous faisiez un appel sur une interface.
Hot Licks

@HotLicks Exactement la même décision que quoi? Si vous compilez une classe pour appeler une méthode foo (String str) sur une interface, le compilateur ne peut pas savoir au moment de la compilation sur quelle classe la méthode foo (String str) doit être appelée. Ce n'est qu'au moment de l'exécution que l'appel de méthode peut être lié à une implémentation de classe particulière.
Aaron

Mais la liaison statique à la signature de méthode spécifique se produirait toujours. Le compilateur choisirait toujours callEat (Animal) sur callEat (Dog).
Hot Licks

@HotLicks Bien sûr, mais ce n'est pas la question à laquelle j'ai répondu. Peut-être que c'était trompeur de ma part: DI l'a comparé à l'invocation sur une interface pour souligner qu'au moment de la compilation, le compilateur ne peut pas savoir si vous avez réellement instancié une sous-classe / implémentation différente ou non.
Aaron

En fait, au moment de la compilation, le compilateur peut (dans ce cas) très facilement savoir que "a" est un chien. En fait, il faudra probablement faire un certain effort pour «oublier» cela.
Hot Licks
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.