2009-08-21 4 views
7

Nehmen wir an, ich habe zwei Klassen namens A und B, die so miteinander verknüpft sind, dass es am praktischsten ist, wenn das Objekt jeder Klasse eine Referenz auf die andere Klasse enthält. Mit anderen Worten, Klasse A hat eine Variable "b" der Klasse B. Klasse B hat eine Variable "a" der Klasse A. Auf diese Weise hat der Code in jeder Klasse einfachen Zugriff auf die andere Klasse.Java: Kann man gegenseitige, endgültige Klassenreferenzen haben?

Gibt es eine Möglichkeit, diese Zuordnung als "endgültig" einzurichten? d.h. Variable b in Klasse A ist endgültig und Variable a in Klasse B ist endgültig? Es scheint, dass das Einrichten dieser Verweise in einem Konstruktor (wie es für das endgültige Schlüsselwort erforderlich wäre) eine unlogische zirkuläre Art der Referenz erfordert.

Dies ist eher eine konzeptionelle als eine praktische Frage. Vielen Dank!

Antwort

6

Wie ein anderes Plakat darauf hingewiesen, ist es ist möglich, dies zu tun, indem eine der Klassen für das Konstruieren der anderen verantwortlich und sich in den Konstruktor für das andere Objekt übergeben.

Dies führt jedoch zu potenziellen Problemen, wenn Sie möchten, dass eine Instanz der ersten Klasse einer Instanz der zweiten Klasse entspricht (was Sie anscheinend tun). Ein anderes Objekt könnte möglicherweise einen vorhandenen Verweis auf die erste Klasse in den Konstruktor für die zweite Klasse übergeben und somit eine ungültige Situation ermöglichen (die Instanz der zweiten Klasse hat nun einen Verweis auf eine Instanz der ersten Klasse, die auf verschiedene verweist Instanz der zweiten Klasse).

Weitere mögliche Probleme treten auf, wenn Sie berücksichtigen, dass der Konstruktor der zweiten Klasse nicht auf nicht initialisierte Member der ersten Klasse verweisen kann.

Mein Vorschlag ist, dass, wenn die zwei Klassen so miteinander verknüpft sind, dass sie wollen, dass sie jeweils letzte Verweise auf die anderen haben, einfach die gleiche Klasse machen. Dann haben Sie immer eine letzte Referenz auf das andere Objekt: this.

+0

Die Kombination der beiden Klassen hilft nicht. Zirkelbezüge können und treten häufig bei Objekten der gleichen Klasse auf. Betrachten Sie Knoten in einer Baumstruktur, in der jeder Knoten einen abschließenden Verweis auf seine Eltern und seine untergeordneten Elemente hat oder versucht, eine unveränderliche doppelt verknüpfte Liste mit abschließenden Verweisen auf die nächsten und vorherigen Knoten zu erstellen. –

6

Ja, es ist möglich, wenn eine der Klassen für das Erstellen der Instanz der anderen Klasse zuständig ist. Der erste Konstruktor kann eine Instanz von sich als Parameter für die zweite Klasse übergeben.

public class A { 
    final B b; 
    public A() { 
     b = new B(this); 
    } 
    public B getB() { 
     return b; 
    } 
} 
public class B { 
    final A a; 
    public B(A a) { 
     this.a = a; 
    } 
} 
+4

Während dieser Ansatz funktioniert, ist es wahrscheinlich gefährlich, da der B-Konstruktor einen Verweis auf ein noch nicht vollständig konstruiertes Element erhält. Nehmen Sie beispielsweise an, dass A ein anderes Feld hatte, das nach * 'b' initialisiert wird und dass B-Konstruktor versucht, darauf zuzugreifen. Es wird ein nicht initialisiertes Objekt erhalten. Das gleiche gilt für Methoden von A from B constructor, oder wenn A später erweitert wird ... Vielleicht wäre ein Redesign eine bessere Option. –

+0

Etwas prägnanter ist es, eine Klasse eine innere Klasse der anderen zu haben.(Hinweis: Da Sie den Konstruktor "B" öffentlich gemacht haben, könnte eine andere Klasse in einem anderen Paket einen anderen "B" für jede Instanz von "A" (oder "null"!) Konstruieren.) –

+0

Vielleicht wäre der beste Weg, beides zu machen Konstruktoren packen den Bereich ein und stellen eine Art von Factory-Methode zur Verfügung, um A bereitzustellen. Auf diese Weise kann niemand versehentlich die Klassen missbrauchen. – extraneon

1

, wenn Sie einen Di Behälter wie guice verwenden, können Sie diese ohne offensichtliche Referenz acieve innerhalb der Konstruktor - was fehleranfällig sein kann, wie zuvor aufgezeigt.

deklarieren Sie die Abhängigkeit a-b und b-a und injizieren Sie eine davon in eine andere Klasse. Guice wird etwas Magie machen, damit beide Felder endgültig sein können. Im Grunde wird zuerst ein Proxy injiziert und später der Delegator gesetzt.

benötigen Sie ein Codebeispiel dafür?