2

在 Java 中,构造函数不能是递归的。编译时错误:“递归构造函数调用”。假设我们没有这个限制。

要记住的事情:

  • 构造函数的返回类型是 void。由于它是一种 void 方法,因此您无法利用递归的全部力量。
  • 构造函数可以使用 this() 调用自身(或任何其他构造函数)。但是“对此的调用必须是构造函数中的第一条语句”
  • 我们可以在连续调用之间使用非本地数据,以从递归构造函数中获得一些可能的收益。

允许递归构造函数有什么好处吗?

4

6 回答 6

9

构造函数(当它们相互调用时)就像返回的方法void。因此,它们产生结果的唯一方法是通过副作用。然后,这仅限于改变他们正在构造的对象或通过改变作为参数传入的值。后者在构造函数中是一个非常讨厌的想法。构造函数通常从其参数中获取信息而不改变它们。

因此,改变正在构造的对象是唯一的选择,以便有任何方法来跟踪递归的进度,以便最终终止。很难看出它比普通构造函数中的简单循环更容易编写、更清晰阅读等等。

从构造函数中调用另一个构造函数(with )当然与在构造函数中使用表达式this完全不同:new

class Node
{
    Node _left, _right;

    public Node(Node left, Node right)
    {
        _left = left != null ? new Node(left._left, left._right) : null;
        _right = right != null ? new Node(right._left, right._right) : null;
    }
}

这里Node构造函数调用自己,但是通过一个新的表达式。这是关键的区别。表达式产生一个new值,因此这纯粹是“功能性”、非变异的东西,并提供了一种方便的方法来制作节点树的深层副本。

于 2010-04-07T18:39:00.497 回答
1

构造函数可以是递归的。(那是在 C# 中,但你可以在 Java 中做同样的事情)

于 2010-04-07T18:47:46.893 回答
1

我们来看看这个问题。首先,调用时会发生什么new MyClass("foo");?好吧,有两件事正在发生。首先,虚拟机将分配存储类型对象所需的内存MyClass。然后,构造函数被调用。构造函数的工作是初始化这个刚刚分配的内存。因此,构造函数根本没有返回类型(甚至没有 void)。new 运算符返回的值是对分配内存的引用,因此构造函数也不能返回。

那么,递归构造函数调用有什么好处。这种调用的唯一好处是像处理其他参数一样处理某些构造函数参数,并通过重新调用构造函数来实现。虽然这是可能的,但通常很容易调整构造函数本身的值(使用非最终参数),然后初始化对象属性(简而言之,您不需要为此递归)。

其次,您可以通过将所有工作卸载到可以根据需要进行递归的工作方法来相当容易地进行递归。

一个更有趣的问题是对 super 或 this 调用的限制是构造函数的第一条语句。这个限制可能是为了阻止草率或不安全的编程实践。声明在此处以粗体显示,因为可以(尽管不美观)绕过此限制。如果您记得表达式可能有副作用(例如变量赋值),并且用于参数的表达式在调用本身之前被调用,则可以创建复杂的表达式,在调用委托构造函数之前完成所有计算。

您希望稍后在构造函数主体中调用委托/超级构造函数的一般原因是参数操作。您可以使用(静态)辅助函数来完成这些计算并提供正确的值。这通常更干净,但并非在所有情况下都如此。实际执行速度不应该受到影响,因为热点可以很好地内联这些东西。

这意味着最终的考虑归结为提供自由放置代表/超级呼叫的灵活性,而不是通过使不正确的做法变得更加困难而提供的额外安全性。Java 的设计者(以及一般的 Java 哲学)做出的选择是,以专家手中的原始语言能力为代价(增加了复杂性),让做错事变得更加困难。做出的选择对我来说是一个有效的选择,尽管我个人想要这种能力(人们总是可以在没有这些限制的 JVM 上实现一种 java++ 语言)。

于 2010-04-08T09:26:21.037 回答
0

您可能无法编写递归构造函数,但可以从构造函数调用递归函数。我以前从来没有这样做过,我想不出你可能需要这样做的情况,但如果你愿意,你可以这样做。

于 2010-04-07T18:39:07.200 回答
0

你是什​​么意思允许?您可以在 Java 中使用递归构造函数。它们允许您重用代码并以更分层的方式设计您的构造函数。

在以下递归构造函数示例中,我可以调用new User()ornew User("Marcus")并且将我使用的任一构造函数newUser设置为true.

public class User() {
  public String userName;
  public boolean newUser;
  User() {
    newUser = true;
  }
  User(String userName) {
    // Recursively call no-argument constructor
    this();
    this.userName = userName;
  }
}

没有递归构造函数也是一样的。注意重复的代码行:

public class User() {
  public String userName;
  public boolean newUser;
  User() {
    newUser = true;
  }
  User(String userName) {
    newUser = true;
    this.userName = userName;
  }
}

在以下非递归构造函数示例中,如果我没有将名称传递给构造函数,则名称将设置为“新用户”。如果我没有设置名称,我只想调用无参数构造函数。如果我在这里进行递归构造函数调用,我最终会设置 userName 两次:

public class User() {
  public String userName;
  User() {
    this.userName = "New User";
  }
  User(String userName) {
    this.userName = userName;
  }
}

只有在以下情况下,您才会使用递归构造函数:

  1. 拥有多个构造函数
  2. 在你的构造函数中有代码
  3. 想要递归地使用另一个构造函数中的代码
于 2010-04-07T19:14:38.793 回答
0

构造函数的返回类型是 void。

不,不是。

构造函数可以使用 this() 调用自身(或任何其他构造函数)

不,它只能调用其他构造函数,并且只有在不会导致递归调用当前构造函数的情况下。这就是为什么您会收到您提到的错误消息。

我们可以在连续调用之间使用非本地数据,以从递归构造函数中获得一些可能的收益。

如何?为什么要重新初始化一个对象?什么时候不能一次按顺序完成?计算机编程 39 年,OO 20 年,从来没有遇到过这个问题。

允许递归构造函数有什么好处吗?

你还没想出什么...

于 2010-04-08T08:49:32.173 回答