8
votes

Java: classe héritante de soi

Je sais que c'est inutile: je viens de le trouver drôle et je veux me renseigner davantage sur les mécanismes de ce qui se passe lorsque vous créez une classe qui hérite de lui-même, entraînant un crash de dépassement de pile. C'est incroyable que Java vous permet de faire une telle construction pour commencer.

Je suis juste deviner, mais la JVM se mettait dans une boucle infinie en essayant de résoudre la classe avant de l'instantir, ou est-ce réellement instancié de plusieurs copies de la classe sans fin?

J'aurais dû être plus précis; J'utilise une classe intérieure pour dériver de la classe enfermante. xxx


11 commentaires

@Turtletoes tellement, qu'est-ce qui s'est passé réellement quand vous avez essayé de le faire?


que veux-tu dire? Je ne peux pas obtenir de héritage cyclique, quelle version utilisez-vous? Hériter.java:1: héritage cyclique impliquant d'abord


Essayer d'hériter de la classe déclarée provoque une erreur de compilation (JDK 1.6 sous Windows / Cygwin). Quelle version de Java utilisez-vous?


Utiliser une classe interne pour hériter de la classe extérieure


Javac n'est pas le seul moyen de créer un fichier de classe. Vous pouvez également modifier un pour obtenir une classe de faux.


Un problème de pensée pour vous; Si toutes les classes s'étendent objet implicitement, qu'est-ce que objet s'étend? ;)


Voir op. J'ai oublié de mentionner que j'extuit une classe intérieure avec la classe entourant. Il compile, court et se bloque avec un débordement de pile


@Turtletoes: Pouvez-vous montrer un exemple complet? Qu'est-ce qui se bloque exactement?


Du constructeur extérieur (), créez une instance d'un objet intérieur.


Je vais mettre à jour avec l'ensemble du code que j'ai ... 1 min


D'accord, j'ai mis à jour un code de travail complet qui va crancer


8 Réponses :


0
votes

Vous pouvez obtenir la réponse par: xxx

de cette façon, il est résolu mais non instancié. Donc, vous pouvez tenter si la résolution elle-même provoque l'accident.

Je suppose que cela dépend de la JVM que vous utilisez.


0 commentaires

0
votes

Quand j'essaie de compiler: xxx

i get: xxx

alors Java ne vous laissez pas faire ce genre de chose . Pour information, java version "1.6.0_24"


3 commentaires

Cela ne concerne pas l'hérité de l'entourage (-1). Il s'agit de la classe intérieure hérite de englober un.


@Vladimir Ma réponse a été écrite avant le code ajouté à la question. Sans le code, il n'ya aucun moyen de savoir que la pop parle des classes intérieures («vous créez une classe qui hérite de lui-même» c'est exactement ce que je fais).


@Krtel, je récupérerais mon bowvote, si la réponse est éditée une fois de plus.



1
votes

Essayez dans un IDE comme Eclipse, il vous permettra de le faire. Ie donne une erreur comme celle-ci.

cycle détecté: le test de type ne peut pas s'étendre / se mettre en oeuvre ou l'un de ses propres types de membres


1 commentaires

Mais c'est l'IDE étant plus intelligent que le compilateur (une bonne chose). Ce que je suis surpris, c'est que le compilateur Java permet une telle construction du tout.




8
votes

N'oubliez pas que, puisque à l'intérieur code> étend extérieur code>, il a un appel implicite à super () code> strong> qui est le constructeur de à l'extérieur code> (qui appelle à son tour le constructeur de à l'intérieur code>) et que cela va donc autour.

Le code que vous avez publié est conceptuellement différent de la Programme suivant FORT>: P>

class A {
    B b = new B();
}

class B extends A {
}

public class Test {
    public static void main(String[] args) {
        new A(); // Create an A...
                 //   ... which creates a B
                 //   ... which extends A thus implicitly creates an A
                 //   ... which creates a B
                 //   ...
    }
}


2 commentaires

Exécutez le code que j'ai publié et vous obtiendrez une erreur de dépassement de pile


Faites une autre classe et essayez d'installer la classe extérieure.



1
votes

Le compilateur Java ne va pas entrer dans une boucle infinie lorsque vous essayez d'entrer une chaîne héritabilité cyclique. Après tout, chaque chaîne d'héritage est un graphique fini (et parlant de manière informelle, avec un très petit nombre de nœuds et de bords.) Plus précisément, le graphe d'héritage de la sous-classe A à (éventuelle) Superclass Z doit être une ligne (pas la Autre moyen, cependant), et le compilateur peut facilement déterminer s'il s'agit d'une ligne ou non.

Il ne faut pas beaucoup pour un programme pour déterminer si un tel petit graphique est cyclique ou non, ou s'il s'agit d'une ligne ou non, ce que le compilateur fait. Donc, le compilateur ne va pas dans une boucle infinie et le JVM ne manque jamais d'espace de pile depuis 1) ni le compilateur ne s'exécute sur la JVM, ni 2) La JVM se met à exécuter (puisque rien ne peut compiler et le compilateur n'applique jamais Dans de telles conditions, la JVM de toute façon.)

Je ne suis au courant d'aucune langue permettant de tels graphiques de héritage cycliques (mais je ne fais rien que Java depuis 11 ans, alors ma mémoire de quoi que ce soit autre que Java soit pâturé.) Je ne peux pas voir, en outre, le utilisation d'une telle construction (dans la modélisation ou la vie réelle). Pourrait être théoriquement intéressant, cependant.

Modifier

OK, j'ai couru votre code et il provoque un débordement d'une pile. Tu avais raison. Je vais devoir m'asseoir et vraiment étudier cela pour comprendre pourquoi le compilateur permet une telle construction.

belle trouvaille !!!!


6 commentaires

Merci ... j'étais juste assis ici à l'outil pour tenter de briser des choses, et ça a cassé


@ luis.Sespinal, il n'y a rien de spécial sur le programme. Le débordement de la pile est due aux deux constructeurs mutuellement récursifs. Voir ma réponse.


Je pensais dehors ... m'a attrapé de garde au début ... toujours, je ne peux pas croire que cela vous permet de faire une référence circulaire aussi flagrante et évidente


@aioobe - Non, non, je sais que les deux constructeurs sont mutuellement récursifs (jolie réponse BTW.) Ce que je dois réfléchir, c'est pourquoi le compilateur laisserait une telle construction. Il ne devrait pas y avoir de raison (sur un modèle valide) pour une chaîne de héritage cyclique ou pour les membres internes d'étendre leurs types contenant. Ce n'est vraiment pas une bonne modélisation, une sémantique douteuse. Je suis surpris que le compilateur le permet du tout. Merci pour la réponse, cependant.


La sémantique est claire: c'est une récursion infinie, qui fonctionnerait pour toujours sur une machine avec une mémoire infinie. En fait, si le compilateur ou la JVM était assez intelligent, cela la compilerait dans tandis que (vrai) {} qui a une sémantique équivalente. Comme vous le réalisez probablement, il y a aussi peu de raisons de interdire ce programme Java, comme il serait d'interdire un tandis que (vrai) {} -pogramme.


Je doute fort que le compilateur prendra l'étape de convertir des fonctions mutuellement récursives en une boucle infinie avec un corps vide. Contrairement au plus tard (dont la détection est déterminable), des fonctions mutuellement récursives peuvent ne pas avoir une condition d'arrêt (la détection dont la détection est indécidable dans quoi que ce soit les cas les plus triviaux.) C'est un bon point, cependant, pourquoi le compilateur ne serait pas interdire de telles constructions. Cela dit, les IDs intelligents dactent pour les conditions de héritage cyclique.



1
votes

L'exemple que vous avez posté pourrait être problématique si nous le changeons un peu plus: xxx

mais ce n'est pas vraiment lié au fait que à l'intérieur est un intérieur Classe de à l'extérieur , cela aurait pu se produire avec des classes de niveau supérieur distinctes identiques.


3 commentaires

Voici un exemple de vie réel où quelqu'un construit quelque chose avec ce problème.


Je dirai qu'un tel exemple est causé par une mauvaise conception. Je ne peux pas comprendre un modèle Valide OO qui contemplerait le héritage cyclique. Comme ce n'est pas un concept qui est même présent dans des systèmes réels, et notre tâche consiste à modéliser et à créer une représentation logicielle de tels systèmes réels, elle suivrait que l'héritage cyclique n'a aucun endroit (ou ne devrait pas avoir.)


Certaines langues OO permettent une héritage cyclique - mais cela implique alors toutes les classes du cycle étant synonymes. ( Cecil est un exemple, si je me souviens bien.) Cela ne concerne toujours pas utiliser l'héritage mélangé avec des classes intérieures.



2
votes

Dans sa forme finale, ce problème n'a rien à voir avec héritage cyclique et classes intérieures. C'est juste une récursion infinie causée par un appel de constructeur récursif non lié. Le même effet peut être montré par l'exemple simple suivant: xxx

Notez que ce code est parfaitement valide, car Java n'applique aucune restriction sur les appels récursifs.

Dans votre cas, il est légèrement plus compliqué du fait de l'héritage, mais si vous vous souvenez que le constructeur de sous-classe appelle implicitement un constructeur de superclasse, il devrait être clair que ces appels forment une récursion infinie.


2 commentaires

Votre exemple ne reflète pas sur le fait qu'une classe s'étend un autre. Voir ma réponse pour un exemple légèrement plus proche des OPS.


Je n'ai pas dormi dans plus de 48 heures et je suis sur ma cinquième canette de Red Bull ... m'a semblé étonnant pour moi au début