Java 如何初始化循环依赖项(相互引用的最终字段)?

Java 如何初始化循环依赖项(相互引用的最终字段)?,java,design-patterns,reflection,dependency-injection,guice,Java,Design Patterns,Reflection,Dependency Injection,Guice,如何初始化此项: class A { final B b; A(B b) { this.b = b; } } class B { final A a; B(A a) { this.a = a; } } DI框架、反射、更好的设计 动机和用例(已添加): 我的特殊用例是简化A和B子类中的字段访问。因此,我注入它们,以便在派生类中通过字段很快引用它们,而无需在每个子类中显式声明 还有一个关于DI的建议是,对象最好

如何初始化此项:

class A {
    final B b;

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

class B {
    final A a;

    B(A a) {
        this.a = a;
    }
}
DI框架、反射、更好的设计

动机和用例(已添加)

我的特殊用例是简化
A
B
子类中的字段访问。因此,我注入它们,以便在派生类中通过字段很快引用它们,而无需在每个子类中显式声明


还有一个关于DI的建议是,对象最好是不可变的:。

您拥有的是循环依赖关系。我能想到的唯一方法是不要将字段声明为final,而是使用setter注入而不是构造函数注入来注入依赖项

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

a.setB(b);
b.setA(a);

你可以使用工厂方法

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

虽然它可能看起来很脏,但我更喜欢用
供应商
(比如
番石榴
Java 8
中的一个)替换其中一个
最终
参考:


我知道,
MutableSupplier
的易变性对于不变性爱好者来说看起来非常丑陋,但我发现在这种情况下使用它或多或少是可以接受的:)

你能举个例子说明A需要访问B,反之亦然吗?更好的设计,当然。我不认为在现实世界的应用程序中会出现这种情况,如果你需要更好的设计:)。。。是的,为什么DChild构造函数没有定义参数,只是为了停止循环?在问题中添加了一个用例@AbdullahShaikh,我可能应该从问题中删除/澄清案例2,因为它确实令人困惑。我认为你做不到。如果没有惰性,就不能创建不可变的递归结构,Java也没有。您必须使其中一个字段成为非最终字段,并提供对它的访问权限。当然,您可以尝试使用反射及其
setAccessible
方法,但这肯定远远不是“更好的设计”,更不用说它不会在所有JVM上都起作用。是的,我也在考虑同样的“最终确定字段”技术。这很直观,但是有一个供应商作为一个字段看起来并不漂亮,每个字段增加了整整17个字节。如果内存消耗很重要,那么我同意使用
供应商
:)嗯,这也很好-我实际上已经开始使用它了。是的,我在客户端GWT代码中经常使用它,因为循环不可变依赖关系经常出现在UI中。或者,您可以将工厂对象传递给的构造函数,而第一个对象将自身传递给工厂“create”方法
A(BFactory BFactory){this.b=BFactory.createB(this)}
。另外请注意:这些解决方案允许
this
在构建过程中“逃逸”,这在某些情况下可能是一种反模式。逃逸是什么意思?
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));
}
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>();
    }

}