webentwicklung-frage-antwort-db.com.de

Wie kann eine Klasse ein Mitglied ihres eigenen Typs haben?

Angenommen, ich definiere eine Klasse, die als Element eine Variable desselben Typs als sich selbst hat.

public class abc {
    private abc p;
}

Das funktioniert eigentlich sehr, sehr zu meiner Überraschung. 

Warum ich denke, dass dies nicht der Fall sein sollte: Das Erstellen einer Instanz von abc enthält eine Variable vom Typ abc, die eine Variable vom Typ abc enthält, die eine Variable vom Typ abc enthält, die .....

Offensichtlich irre ich mich. Könnte mich jemand aufklären, wie?

37
jason

Sie deklarieren nur die Variable und erstellen sie nicht. Erstellen Sie es bei der Deklaration oder im Konstruktor und lassen Sie mich wissen, was passiert: 

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

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

Wenn Sie es nicht in der Klasse erstellen, sondern einen Verweis darauf in einer Getter-Methode oder einem Konstruktorparameter akzeptieren, funktioniert Ihr Code übrigens gut. So funktionieren einige verknüpfte Listen.

Der Unterschied liegt in der Kompilierungszeit gegenüber den Laufzeitprüfungen.

Im ersten Fall (Kompilierzeit) erklären Sie, dass Sie in diesem Fall einen Verweis auf einen Wert vom Typ abc haben. Dies wird dem Compiler bekannt sein, wenn er nach der richtigen Semantik sucht, und da er den Typ zum Zeitpunkt der Kompilierung kennt, sieht er kein Problem damit.

Im zweiten Fall (Laufzeit) erstellen Sie tatsächlich einen Wert, auf den sich diese Referenz bezieht. Hier könnten Sie möglicherweise selbst in Schwierigkeiten geraten . Zum Beispiel, wenn Sie folgendes sagten:

public class abc {
    private abc p;

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

Dies kann genau aus dem von Ihnen angegebenen Grund zu Problemen führen (Rekursion, die keinen Basisfall enthält und kontinuierlich Speicher reserviert, bis Sie VM nicht mehr über genügend Heapspeicherplatz verfügen).

Sie können jedoch noch etwas Ähnliches tun und die unendliche Rekursion vermeiden. Indem Sie vermeiden, den Wert während der Erstellung zu erstellen, verschieben Sie ihn, bis eine Methode aufgerufen wird. Tatsächlich ist dies eine der gebräuchlichsten Möglichkeiten, das Singleton-Muster in Java zu implementieren. Zum Beispiel:

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;
    }
}

Dies ist absolut gültig, da Sie nur eine neue Instanz des Werts erstellen. Da zur Laufzeit die Klasse bereits geladen wurde, weiß sie, dass der Typ der Instanzvariablen gültig ist.

19
pseudoramble

Wenn Sie einige reale Szenarien modellieren möchten, müssen Sie möglicherweise diese Vorstellung verwenden. Denken Sie zum Beispiel an den Zweig eines Baums. Der Zweig eines Baums kann n Anzahl von Zweigen enthalten. Oder denken Sie aus dem Hintergrund der Informatik an den Knoten einer verknüpften Liste. Ein Knoten hat eine Referenz auf den daneben liegenden Knoten. Am Ende enthält die nächste eine Null, um das Ende der Liste anzuzeigen.

Dies ist also nur eine Referenz, die darauf hinweist, dass sich dieser Typ auf einen eigenen Typ beziehen kann. Nichts mehr.

10
ring bearer

Laut der gewählten Antwort ist dies in Ordnung, da die Variable nicht instanziiert wird, sondern eine Referenz von einer Set-Methode oder einem Konstruktor-Argument akzeptiert. Sie sehen also, es ist nur eine Referenz, nur eine Adresse.

Es gibt jedoch eine Möglichkeit, es zu instanziieren, ohne die Albtraumschleife zu verursachen, und zwar als statisch. Diese Art verbiegt die Rekursionsregel, da sich ein statischer Member nicht auf Objektebene, sondern auf Klassenebene befindet.

So...

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

Funktioniert gut, da die Variable 'p' nicht wirklich von der Instantiierung der abc-Klasse beeinflusst wird. Es ist fast das Äquivalent zum Erstellen des 'p'-Objekts in einer anderen Klasse.

Denken Sie daran, dass ich das tun kann ...

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

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

Ohne das Erstellen eines neuen Objekts von abc in der Hauptmethode. So funktioniert Statik, sie ist praktisch von der Instantion von Objekten nicht betroffen. Bei Ihrer Frage bilden sie die Ausnahme von der Rekursionsregel.

1
WTRIX

Einige Antworten hier zusammenfassen.

Die Klasse enthält einen Hinweis auf eine eigene Art. Die beste Analogie in der realen Welt wäre eine Schatzsuche. Ein Hinweisplatz enthält einige Daten und einen Hinweis, der zu einem anderen Hinweisplatz führt, von dem Sie wissen, dass er sich wiederholt.

Es heißt nicht, dass ein Hinweisplatz einen anderen Hinweisplatz enthält, auf den Sie scheinbar schließen.

1
Rakesh Horkeri

Während wir über "Rekursion" sprechen, ist die "Ausführung" von Code der Punkt, den wir in Betracht ziehen. Ja, es ist "dynamisch", wenn eine "Rekursion" auftritt.

In der Typdeklaration ist eine Variable, die ein Element enthält, also der Typ der enthaltenden Variablen, "statisch". Beispielsweise kann eine Box eine andere Box enthalten und so weiter. Aber es gibt endlich eine kleinste Box, die nichts enthält. Bei einer Kette von Eisenbahnwagen hat jeder Wagen außer dem letzten Wagen ein Mitglied für den nächsten Wagen.

Im Programm müssen die statischen Daten "endlich" sein (Sie haben keinen "unendlichen" Speicherplatz). Die Ausführung von Code kann "unendlich" sein. Es gibt aber eine Ausnahme. Stellen Sie sich nur einen Kreis einer Kette vor.

0
Mike Lue