8

How do you initialize this:

class A {
    final B b;

    A(B b) {
        this.b = b;
    }
}

class B {
    final A a;

    B(A a) {
        this.a = a;
    }
}

DI framework, reflection, better design?

Motivation and a use case (added):

My particular use case is simplifying field access in A's and B's sub-classes. So I'm injecting them to shortly reference them by fields in the derived classes without a need to declare explicitly in each sub-class.

There is also a recommendation on DI that objects should better be immutable: Guice best practices and anti-patterns.

Community
  • 1
  • 1
Andrey Chaschev
  • 16,160
  • 5
  • 51
  • 68
  • Could you give an example where A needs access to B and vice versa? – helpermethod Nov 06 '13 at 09:35
  • 4
    Better design, definitely. – Kayaman Nov 06 '13 at 09:39
  • I don't think you will get this kind of scenario in real world applicaton and if you then you need a better design :) ... and yes why the DChild constructor has not defined arguments, just to stop the loop ? – Abdullah Shaikh Nov 06 '13 at 09:47
  • Added a use-case to the question. @AbdullahShaikh, I should probably remove/clarify case 2 from the question as it's really confusing. – Andrey Chaschev Nov 06 '13 at 09:58
  • I don't think you can do it. You cannot create immutable recursive structures in the absence of laziness, and Java does not have it. You have to make one of the fields non-final and provide access to it. Of course, you can try and use reflection and its `setAccessible` method, but this will definitely be far from "better design", not to mention it won't work on all JVMs. – Vladimir Matveev Nov 06 '13 at 10:25

3 Answers3

6

You could use a factory method

class A {
    final B b;

    A(B b) {
        this.b = b;
    }
}

abstract class B {
    final A a;

    B() {
        this.a = constructA();
    }

    protected abstract A constructA();
}

public class C {
    public static void main(String []args){
        new B(){
            protected A constructA(){
                return new A(this);
            }
        };
    }
}
Matthew Mcveigh
  • 5,695
  • 22
  • 22
  • Alternatively, you could pass a Factory object to A's constructor and the first object passes itself to the Factory "create" method. `A(BFactory bFactory) { this.b = bFactory.createB(this) }`. Note also: these solutions allow `this` to "escape" during construction, which can be an anti-pattern in some situations. – Brian Vosburgh Jun 09 '16 at 13:36
  • What do you mean with escape? – testo Nov 06 '18 at 22:34
1

Though it may look dirty, but I prefer to replace one of the final references with Supplier (like one in Guava or Java 8) like:

class A {
    final Supplier<B> b;

    A(Supplier<B> b) {
        this.b = b;
    }

    // keeping this constructor just for usability's sake
    A(B b) {
        this.b = ofInstance(b); // using Guava's Suppliers.ofInstance here
    }
}

class B {
    final A a;

    B(A a) {
        this.a = a;
    }
}

public static void main(String[] args) {
    // using MutableSupplier.create() static factory method
    MutableSupplier<B> bRef = create();
    A a = new A(bRef);
    B b = bRef.set(new B(a));
}

where MutableSupplier looks somehow like the following:

import com.google.common.base.Supplier;

public class MutableSupplier<T> implements Supplier<T> {

    private boolean valueWasSet;

    private T value;

    private MutableSupplier() {
    }

    @Override
    public T get() {
        if (!valueWasSet) {
            throw new NullPointerException("Value has not been set yet");
        }
        return value;
    }

    public T set(final T value) {
        if (valueWasSet) {
            throw new IllegalStateException("Value has already been set and should not be reset");
        }
        this.value = value;
        this.valueWasSet = true;
        return value;
    }

    public static <T> MutableSupplier<T> create() {
        return new MutableSupplier<T>();
    }

}

I know that MutableSupplier's mutability looks super-ugly for immutability enthusiasts but I found that using it is more or less acceptable in such cases :)

Yuriy Nakonechnyy
  • 3,742
  • 4
  • 29
  • 41
0

What you are having is a circular dependency. The only way I can think of is to not declare the fields as final and have your dependency injected using setter injection instead of constructor injection.

A a = new A();
B b = new B();

a.setB(b);
b.setA(a);
Abdullah Shaikh
  • 2,567
  • 6
  • 30
  • 43