【问题标题】:Pass object that needs `this` in super constructor?在超级构造函数中传递需要“this”的对象?
【发布时间】:2019-10-15 18:55:57
【问题描述】:

我正在设计一个类层次结构,我在标题中遇到了这个问题,这里是它的一个较小的抽象:

public abstract class A {
    private C c;

    public A(C c) {
        this.c = c;
    }

    // ...
}

public class B extends A {
    private int a;
    private int b;
    // ...

    public B(int a, int b, ... /* and more but no object of C */) {

        super(new C(this)); // <-- this is the point, got error:
                            //   can't access `this` .

        this.a = a;
        this.b = b;
        // ...
    }
}

public class C {
    private A a;

    public C(A a) {
        this.a = a;
    }
}

简而言之:A 需要保持对C 的引用,反之亦然,但是两者都应该在它们的构造函数中完成,并且在两者都构造完成之后,字段A aC c 不应该是改变了,所以我让他们private。那么在这种情况下,最佳做法是什么?

下面其实不是一个很好的例子,但重点是我有一种情况需要做上面尝试做的事情。

(更多上下文:现在假设我将class Aprivate C c; 更改为

private List<C> someCs;

即:AC 的容器,因此在某些情况下需要对这些C 进行排序。对于每个C 的某些客户端,客户端需要知道它的父级是什么,即它属于哪个A。在这种情况下,我需要两个方向的参考。)

【问题讨论】:

  • 我认为您需要重新考虑您的设计决策,您确定只有一个方向的耦合还不够吗?或者至少只需要在构造函数中设置一个?另外,对于无法更改的实例变量,最好使用final
  • @JoakimDanielson:让我提供更多背景信息。已更新。
  • 类之间存在固有的循环依赖关系。因此,它不再是等级制度。我们应该先看看如何删除它。
  • @AnindyaDutta:所以这通常是一种难闻的代码?
  • 鉴于更新我认为不需要在任一类的构造函数中设置它,您在构造函数中的 A 中创建列表但您真的没有列表的内容吗?当 C 对象添加到列表中时,C 更可能需要从 C 内部调用的方法 setParent(以 this 作为参数)。想一想,既然列表是 A 中的实例变量,为什么 A 的实例会保存 C 的任何其他实例而不是它的父级实例?

标签: java


【解决方案1】:

this 在构造函数完成之前不是其他对象的有效引用。出于这个原因,这种循环依赖(父引用子,子引用父)不能在构造函数中初始化。父子引用也不能是最终的,因为最终必须在构造函数中初始化。

您可以编写一个初始化所有父母和孩子的工厂。然后在所有初始化之后,您可以交叉引用它们。但这需要工厂可以访问child.parentparent.child。他们要么是公开的,要么有公开的二传手。你可以做一些其他的特技来让工厂在父母和孩子中设置一个私有变量,但我不建议这样做。

这里有一个针对您的情况的建议:创建一个对象来跟踪父母和孩子之间的映射。映射有方法getParent(this) 在需要父级时从子级调用。以及方法 getChildren(this)getChildrenSorted(this) 在需要其子级时从父级调用。

映射可以初始化一次,然后使用 private 和 final 锁定,并防止在运行时进一步更改。然后每个父母和孩子都可以获得对该对象的私有和最终引用。

更多信息:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-29
    • 1970-01-01
    • 2011-10-21
    • 2020-05-24
    • 2014-10-24
    • 1970-01-01
    相关资源
    最近更新 更多