使用最终成员对两个类进行子类化,这些成员相互引用

时间:2013-06-17 01:09:07

标签: java subclass

说我有两个这样的课程

public class Foo
{
    protected final Bar child;

    public Foo()
    {
        child = new Bar(this);
    }
}

public class Bar
{
    protected final Foo parent;

    public Bar(Foo parent)
    {
        this.parent = parent;
    }
}

我想创建一个FooFoo2的子类,其子类为Bar2,它是Bar的子类。我可以这样做:

public class Foo
{
    protected final Bar child;

    public Foo()
    {
        child = new makeChild();
    }

    protected Bar makeChild()
    {
        return new Bar(this);
    }
}

public class Foo2 extends Foo
{
    @Override
    protected Bar makeChild()
    {
        return new Bar2(this);
    }
}

然而,this is supposed to be a very bad idea。但是这样的事情是行不通的:

public class Foo
{
    protected final Bar child;

    public Foo()
    {
        this(new Bar(this));
    }

    protected Foo(Bar child)
    {
        this.child = child;
    }
}

因为new Bar(this)在调用超类型构造函数之前引用this

我认为有两种方法可以解决这个问题:

1)我可以将成员设置为private和non-final,然后使setter在设置时抛出异常,但这看起来很笨拙,只能在运行时检测到任何编码问题。

2)我将Foo构造函数作为参数使用Class类型的Bar对象,然后使用反射来调用该类的构造函数。然而,这似乎是我正在努力做的重量级。

我缺少任何编码技术或设计模式吗?

3 个答案:

答案 0 :(得分:2)

  

我缺少任何编码技术或设计模式吗?

我想到了dependency injection模式。只需从构造函数中取出参数,创建构成上面接口类的类型,然后在需要时注入适当的具体类型。

Foo界面

interface Foo {
  public void setBar(Bar bar);
  public Bar getBar();
}

条形码界面

interface Bar {
  public void setFoo(Foo foo);
  public Foo getFoo();
}

我一直在寻找使用Guice或类似的东西来进一步解耦并自动进行注射。

答案 1 :(得分:1)

你的问题是关于循环参考的简单问题。

首先,您应该设计类以避免循环引用,原因有多种here

接下来,如果您没有选项,那么最佳解决方案是使用“依赖注入”。为什么?这样想:

  1. 您必须创建Foo对象
  2. 在创建foo时,您还需要创建Bar
  3. 但是你不想出于明显的原因对Bar实例进行硬编码
  4. 由于您提到的原因
  5. ,在构造函数中调用'overridable'方法是有问题的

    因此,依赖注射可以安全救援。

答案 2 :(得分:0)

其中一个选择是为初始化创建一个单独的函数,并在子类ctor中调用init方法:

e.g。

class Foo {
    private Bar child;

    public Foo() {
        initWithChild(new Bar(this));
    }

    protected final void initWithChild(Bar child) {
        this.child = child;
    }
}

class Foo2 {
    public Foo2() {
        initWithChild(new Bar2(this));
    }
}

当然,它假设Foo和Bar是耦合的,依靠Foo来实例化Bar是合理的。但是在大多数情况下,您应该考虑通过DI框架向Bar注入Bar。


在评论中你提到了最终的需要。

虽然我不认为在成员var现在是私有的情况下非终结是一个大问题,但这是另一种方法(实际上与上述相似),你可以考虑 IF 初始化工作就像你的问题一样简单:

class Foo {
    protected final Bar child;

    public Foo() {
        this.child = new Bar(this);
    }
}

class Foo2 {
    public Foo2() {
        this.child = new Bar2(this);
    }
}

没有测试过代码,但我相信它应该可行:P