简体   繁体   中英

Java: how to create instances of two different classes in a third class and pass references to each other through the constructors

This is more of a theoretical question. Suppose I have 3 classes A, B and C. What I want to do is this:

public class A {
  B b = new B(c);
  C c = new C(b);
}

public class B {
  public B(C c) {

  }
}

public class C {
  public C(B b) {

  }
}

I know this code wont work. So, is there another way to do it?

There are many ways to deal with it, none of them ideal:

The deferred-construction approach:

B b = new B();   
C c = new C();
b.setC(c);
c.setB(b);       // until this point, initialization is not complete

The break-the-cycle approach:

B b = new B();   // B is not fully initialized until later
C c = new C(b);
b.setC(c);       // everything set

The one-sets-the-other approach:

B b = new B();   // internally initializes its 'C' instance
C c = b.getC();  // uses the C instance set by B

// inside B
public B() {
   c = new C(this);  // leaking 'this' in constructor, not ideal
}

And then there is the Recommended Way (TM):

D d = new D(); // isolates what B needs from C and C needs from B
B b = new B(d);
C c = new C(d);

which is based on the observation that there is typically no need for B and C to depend fully on each other - you can take the common part and isolate it D , and share that .

There is no way if you insist to pass the other class instance via the constructor. It is classical chicken and egg problem.

To break this you need to add a method in one of the classes to set the reference at a later time, after construction.

Not through the constructors I'm afraid. The conventional way to go about it, would be to create the appropriate setters in B and C and then use those:

public class A {
    B b = new B();
    C c = new C();

    {
        b.setC(c);
        c.setB(b);
    }
}

Introducing this kind of circular reference is a serious design smell though.

Circular dependencies are always worth avoiding. There is no direct way to achieve what you have tried to do via constructors, which is a good thing as this is a code smell and it suggests that some thought time in designing out the circular reference would be worth while.

However if you are determined then the solution is to set the back link after construction. Either via a lazy fetcher, or using a setter like this:

public class A {  
    B b = new B();
    C c = new C(b);

    { 
      b.setC(c);
    }
}

class B {
    public B() {

    }

    public void setC( C c ) {}
}

class C {
    public C(B b) {

    }
}

You are correct, you can't do this. Something else that is unsafe as well so DO NOT DO THIS is:

public class A {
  B b = new B(this);
  C c = new C(this);
}

public class B {
  public B(A a) {

  }
}

public class C {
  public C(A a) {

  }
}

This sort of circular dependency can only be resolved by using a setter method to initialize either B or C after the other one has been created.

However the very fact you need to do this suggests to me that there is an architectural problem here, these classes are far too tightly coupled and you really need to think about design.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM