web-dev-qa-db-fra.com

Pourquoi Java lie-t-il des variables au moment de la compilation?

Considérez l'exemple de code suivant

class MyClass {
    public String var = "base";

    public void printVar() {
        System.out.println(var);
    }
}

class MyDerivedClass extends MyClass {
    public String var = "derived";

    public void printVar() {
        System.out.println(var);
    }
}

public class Binding {
    public static void main(String[] args) {
        MyClass base = new MyClass();
        MyClass derived = new MyDerivedClass();

        System.out.println(base.var);
        System.out.println(derived.var);
        base.printVar();
        derived.printVar();
    }
}

il donne la sortie suivante

base
base
base
derived

Les appels de méthode sont résolus au moment de l'exécution et la méthode remplacée correcte est appelée, comme prévu.
L'accès aux variables est plutôt résolu au moment de la compilation comme je l'ai appris plus tard. Je m'attendais à une sortie

base
derived
base
derived

car dans la classe dérivée, la redéfinition de var fait de l'ombre à celle de la classe de base.
Pourquoi la liaison des variables se produit-elle au moment de la compilation et non au moment de l'exécution? Est-ce uniquement pour des raisons de performances?

63

La raison est expliquée dans la spécification de langage Java dans un exemple de Section 15.11 , citée ci-dessous:

...

La dernière ligne montre qu'en effet, le champ auquel on accède ne dépend pas de la classe d'exécution de l'objet référencé; même si s contient une référence à un objet de classe T, l'expression s.x fait référence au champ x de la classe S, car le type de l'expression s est S. Les objets de la classe T contiennent deux champs nommés x, un pour la classe T et un pour sa superclasse S.

Ce manque de recherche dynamique pour les accès aux champs permet aux programmes d'être exécutés efficacement avec des implémentations simples. La puissance de la liaison et du remplacement tardifs est disponible, mais uniquement lorsque des méthodes d'instance sont utilisées...

Alors oui, la performance est une raison. La spécification de la façon dont l'expression d'accès au champ est évaluée est la suivante:

  • Si le champ n'est pas static:

    ...

    • Si le champ est un final non vide, le résultat est la valeur du champ membre nommé de type T trouvé dans l'objet référencé par la valeur de Primary.

Primaire dans votre cas fait référence à la variable derived qui est de type MyClass.

Une autre raison, comme l'a suggéré @Clashsoft, est que dans les sous-classes, les champs ne sont pas remplacés, ils sont cachés. Il est donc logique d'autoriser l'accès aux champs en fonction du type déclaré ou à l'aide d'un transtypage. Cela est également vrai pour les méthodes statiques. C'est pourquoi le champ est déterminé en fonction du type déclaré. Contrairement à la substitution par les méthodes d'instance, cela dépend du type réel. La citation JLS ci-dessus mentionne en effet implicitement cette raison:

La puissance de la liaison et du remplacement tardifs est disponible, mais uniquement lorsque des méthodes d'instance sont utilisées.

45
manouti

Bien que vous ayez raison sur les performances, il existe une autre raison pour laquelle les champs ne sont pas distribués dynamiquement: vous ne pourrez pas accéder à MyClass.var du tout si vous aviez une instance MyDerivedClass.

En règle générale, je ne connais aucun langage de type statique ayant en fait une résolution variable dynamique. Mais si vous en avez vraiment besoin, vous pouvez faire des getters ou des méthodes d'accesseur (ce qui devrait être fait dans la plupart des cas pour éviter les champs public, de toute façon):

class MyClass
{
    private String var = "base";

    public String getVar() // or simply 'var()'
    {
        return this.var;
    }
}

class MyDerivedClass extends MyClass {
    private String var = "derived";

    @Override
    public String getVar() {
        return this.var;
    }
}
25
Clashsoft

Le comportement polymorphe du langage Java fonctionne avec les méthodes et non avec les variables membres: ils ont conçu le langage pour lier les variables membres au moment de la compilation.

5
alainlompo

En Java, c'est par conception. Parce que la configuration des champs à résoudre dynamiquement rendrait les choses un peu plus lentes. Et en réalité, il n'y a aucune raison de le faire. Depuis, vous pouvez créer vos champs dans n'importe quelle classe privé et y accéder avec méthodes qui sont résolus dynamiquement.

Ainsi, les champs sont mieux résolus à au moment de la compilation à la place :)

2
NIKHIL CHAURASIA