允许递归构造函数可能有什么好处(如果有)?

发布于 2024-08-27 20:45:42 字数 282 浏览 10 评论 0原文

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

要记住的事情:

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

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

In Java, constructors cannot be recursive. Compile time error: "recursive constructor invocation". Let's assume that we did not have this restriction.

Things to keep in mind:

  • The return type of a constructor is void. Since it is a void method you can't harness the complete power of recursion.
  • A constructor can invoke itself (or any other constructor) using this(). But a "call to this must be first statement in constructor"
  • We could use non local data between consecutive calls to still have some possible gain from recursive constructors.

Would there be any benefit from allowing recursive constructors?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

北城孤痞 2024-09-03 20:45:42

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

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

从构造函数中调用另一个构造函数(使用 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 表达式会生成一个值,因此这纯粹是“函数式”、非变异的东西,并提供了一种制作节点树的深层副本的便捷方法。

Constructors (when they are calling each other) are like methods that return void. Consequently the only way they can produce results is by side-effects. This is then limited to mutating the object they are constructing or by mutating the values passed in as parameters. The latter is a pretty nasty idea in a constructor; a constructor usually takes information from its parameters without mutating them.

So mutating the object being constructed is the only option in order to have any way to track the progress of the recursion, in order for it to terminate eventually. And it's very hard to see how that would be easier to write, clearer to read, etc. than a simple loop inside an ordinary constructor.

Calling another constructor (with this) from within a constructor is of course totally different from using a new expression within a constructor:

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

Here the Node constructor calls itself, but via a new expression. This is the crucial difference. A new expression produces a value, so this is purely "functional", non-mutating stuff, and provides a convenient way to make a deep copy of the tree of nodes.

瘫痪情歌 2024-09-03 20:45:42

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

Constructors can be recursive. (That's in C#, but you can do the same thing in Java)

无人问我粥可暖 2024-09-03 20:45:42

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

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

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

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

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

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

Let's look at this problem. First of all, what happens when you invoke new MyClass("foo");? Well there are two things happening. First of all, the virtual machine will allocate the memory needed to store an object of type MyClass. Then, the constructor gets called. The job of a constructor is to initialise this just allocated memory. Therefore, a constructor does not have a return type at all (not even void). The value returned by the new operator is a reference to the allocated memory, so the constructor can not return as well.

Then, what would be the benefit of recursive constructor invocation. The only benefit of such invocation would be to handle certain constructor parameters like others, and doing so by re-invoking the constructor. While this is possible it is generally easy just to adjust the values in the constructor itself (using non-final parameters), and after that initialise the object attributes (in short you don't need recursion for this).

Second, you can do recursion fairly easily by offloading all the work to a worker method that can recurse as much as you want.

A more interesting question is the restriction on super or this invocation being the first statement of the constructor. This restriction was probably put in to discourage sloppy or unsafe programming practices. Statement is put in bold here though as it is possible (although not beautiful) to work around this restriction. If you remember that expressions may have side effects (e.g. variable assignments), and expressions used for parameters are invoked before the call itself it is possible to create complicated expressions that do all your calculations before invoking the delegate constructor.

The general reason why you want to have a delegate/super constructor invocation later in the constructor body is parameter manipulation. You can do that with (static) helper functions that do these calculations and provide the correct values. This is generally cleaner but not in all cases. The actual execution speed should not be affected as hotspot can inline these things very well.

That means that in the end the consideration boils down to providing the flexibility of free placement of delegate/super calls versus the added safety provided by making incorrect practices quite much harder. The choice made by Java's designers (and the general Java philosophy) is to go for making it harder to do the wrong things at the cost of raw language power at the hand of experts (with increased complexity). The choice made is to me a valid one albeit I personally would like the power (one can always implement a java++ language on the JVM that does not have these restrictions).

凉世弥音 2024-09-03 20:45:42

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

You might not be able to write a recursive constructor, but you can call a recursive function from your constructor. I have never had to do this before, and I can't think of a situation where you might need to, but you can do it if you want.

一袭水袖舞倾城 2024-09-03 20:45:42

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

在下面的递归构造函数示例中,我可以调用 new User()new User("Marcus") 并使用我使用的任一构造函数 newUser code> 设置为 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;
  }
}

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

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

如果满足以下条件,您将只使用递归构造函数:

  1. 有多个构造函数
  2. 在构造函数中有代码
  3. 希望递归地使用另一个构造函数中的代码

What do you mean allow? You can have recursive constructors in Java. They allow you to reuse code and design your constructors in a more hierarchical fashion.

In the following recursive constructor example, I can call new User() or new User("Marcus") and with either constructor that I use, newUser is set to 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;
  }
}

Here's the same thing without recursive constructors. Notice the duplicate line of code:

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

In the following non-recursive constructor example, if I don't pass a name into the constructor, then the name is set to "New User". I would only want to call the no-argument constructor if I'm not setting the name. If I did a recursive constructor call here, I would end up setting the userName twice:

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

You will only use recursive constructors if you:

  1. Have more than one constructor
  2. Have code in your constructors
  3. Want to recursively use code that's in another constructor
离线来电— 2024-09-03 20:45:42

构造函数的返回类型是
无效。

不,不是。

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

。否。它只能调用其他构造函数,并且仅当这不会导致当前构造函数的递归调用时。这就是您收到您提到的错误消息的原因。

我们可以在之间使用非本地数据
连续通话仍有一些
递归可能带来的好处
构造函数。

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

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

你还没有想出任何...

The return type of a constructor is
void.

No it isn't.

A constructor can invoke itself (or any other constructor) using this()

No. It can only invoke other constructors, and only if that won't lead to a recursive invocation of the current constructor. That's why you get the error message you referred to.

We could use non local data between
consecutive calls to still have some
possible gain from recursive
constructors.

How? Why would you want to re-initialize an object? When can't you do it sequentially in one pass? Never had this problem in 39 years of computer programming, and 20 years of OO.

Would there be any benefit from
allowing recursive constructors?

You haven't come up with any ...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文