web-dev-qa-db-fra.com

Comment une classe peut-elle avoir un membre de son propre type, n'est-ce pas une récursion infinie?

Disons que je définis une classe qui a pour membre une variable du même type que celle-ci.

public class abc {
    private abc p;
}

Cela fonctionne réellement, à ma grande surprise. 

Pourquoi je pense que cela ne devrait pas: créer une instance de abc, elle contient une variable de type abc, contenant une variable de type abc, contenant une variable de type abc, qui .....

Évidemment je me trompe, quelqu'un pourrait-il m'éclairer sur comment?

37
jason

Vous ne déclarez que la variable et ne la créez pas. Essayez de le créer à la déclaration ou dans le constructeur et laissez-moi savoir ce qui se passe: 

public class Abc {
   private Abc p = new Abc(); // have fun!

   public static void main(String[] args) {
      new Abc();
   }
}

Incidemment, si vous ne le créez pas dans la classe, mais acceptez plutôt une référence dans une méthode getter ou un paramètre constructeur, votre code fonctionnera parfaitement. Voici comment fonctionnent certaines listes chaînées.

32

La différence réside dans les contrôles au moment de la compilation et au moment de l'exécution. 

Dans le premier cas (temps de compilation), vous déclarez que vous aurez une référence à une valeur de type abc dans cette instance. Le compilateur en sera conscient lorsqu'il vérifiera la sémantique appropriée et, puisqu'il connaît le type lors de la compilation, il ne voit aucun problème à cela.

Dans le second cas (exécution), vous créerez en fait une valeur à laquelle cette référence se référera. C’est là que vous pourriez potentiellement vous attirer des ennuis. Par exemple, si vous avez dit ce qui suit:

public class abc {
    private abc p;

    public abc() {
        p = new abc();
    }
}

Cela pourrait vous poser des problèmes pour la raison exacte que vous avez citée (récursivité qui ne contient aucun scénario de base et allouera continuellement de la mémoire jusqu'à ce que vous ayez épuisé l'espace mémoire VM).

Cependant, vous pouvez toujours faire quelque chose de similaire et éviter la récursion infinie. En évitant de créer la valeur lors de la construction, vous la retardez jusqu'à ce qu'une méthode soit appelée. En fait, c’est l’un des moyens courants d’implémenter le modèle singleton en Java. Par exemple:

public class abc {
    private abc p;

    private abc() {  // Private construction. Use singleton method
    }

    public static synchronized abc getInstance() {
        if (p == null)
            p = new abc();

        return p;
    }
}

Ceci est parfaitement valide car vous ne créez qu'une seule instance de la valeur et, puisque l'exécution aura déjà chargé la classe, il saura que le type de la variable d'instance est valide.

19
Doug Swain

Lorsque vous souhaitez modéliser des scénarios réels, vous devrez peut-être utiliser cette notion. Par exemple, pensez à une branche d'arbre. Une branche d'arbre peut avoir n nombre de branches. Ou, venant de l’informatique, pensez au nœud d’une liste chaînée. Un nœud aura une référence au nœud à côté. À la fin, le prochain contiendra un null pour indiquer la fin de la liste.

Donc, ceci est seulement une référence, indiquant que ce type pourrait faire référence à l'un de ses propres Rien de plus.

10
ring bearer

Comme l'indique la réponse choisie, cela ne pose aucun problème, car la variable n'est pas instanciée, mais accepte plutôt une référence provenant d'une méthode set ou d'un argument constructeur. Donc vous voyez, c'est juste une référence, juste une adresse.

Cependant, il existe un moyen de l'instancier sans provoquer la boucle cauchemardesque, et ce, en le déclarant comme statique. Cette sorte de courbe de la règle de récurrence, car un membre statique n'est pas au niveau de l'objet mais au niveau de la classe.

Alors...

public class abc
{
    public static abc p = new abc ();
}

Fonctionne très bien car la variable 'p' n'est pas vraiment affectée par l'instanciation de la classe abc. C'est presque l'équivalent de créer l'objet 'p' dans une autre classe.

Rappelez-vous que je peux le faire ...

 public class abc
 {
     public static abc p = new abc ();

     public static void main(String [] args)
     {
         abc.p;
     }
 }

Sans créer un nouvel objet de abc dans la méthode principale. C’est ainsi que fonctionnent les statistiques, elles ne sont pratiquement pas affectées par l’instantané d’objets et, dans le cas de votre question, elles constituent l’exception à la règle de récurrence.

1
WTRIX

Résumant certaines des réponses ici.

La classe contient une référence à un de ses propres espèces. La meilleure analogie dans le monde réel serait une chasse au trésor. Un lieu d'indice contient des données et un indice qui mènent à un autre lieu d'indice que vous savez répéter.

Cela ne veut pas dire qu’un lieu d’indice a un autre endroit, le genre dont vous semblez déduire.

1
Rakesh Horkeri

Alors que nous parlons de "récursion", "l'exécution" du code est le point que nous considérons. Oui, il est "dynamique" si une "récursivité" se produit.

Dans la déclaration de type, une variable qui contient un membre, qui est le type de la variable contenant, est "statique". Par exemple, une boîte peut contenir une autre boîte à l'intérieur, etc. Mais il y a finalement une plus petite boîte qui ne contient rien. Pour une chaîne de wagons de chemin de fer, chaque voiture a un membre pour le prochain transport sauf le dernier.

En programme, les données statiques doivent être "finies" (vous n'avez pas d'espace mémoire "infini"). L'exécution du code peut être "infinie". Mais il y a une exception. Imaginez juste un cercle de chaîne.

0
Mike Lue