2010-04-07 33 views
2

在Java中,构造函数不能递归。编译时间错误:“递归构造函数调用”。假设我们没有这个限制。允许递归构造函数有什么好处(如果有的话)?

事情要记住:

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

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

+0

当然,构造函数是一种方法,返回被实例化的类的新实例,而不是null? – Finbarr 2010-04-07 18:41:16

+0

@Finbarr不,这将是一种工厂方法;构造函数本身没有返回值。 – 2010-04-07 18:42:45

+0

@Finbarr - 没有人提到'null'!并且构造函数严格地说与其他方法不一样,所以将它们精确地描述为具有返回类型是没有意义的。构造函数的主体不返回任何东西,当一个构造函数调用另一个构造函数时,没有中间返回值,所以最接近的类比是返回'void'的方法。 – 2010-04-07 18:43:05

回答

9

构造函数(当它们互相调用时)就像返回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表达式产生一个值,所以这是纯粹的“功能”,非变异的东西,并提供了一个方便的方法来制作节点树的深层副本。

0

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

+0

您可能已经完成了它,而没有将其视为递归 - 例如,传入构造函数的对象的深层克隆。 – 2010-04-07 18:41:25

1

Constructors can be recursive。 (这是C#,但你可以在Java中做同样的事情)

+1

构造*离子*可以是递归的,如果在构建过程中包含构建其他对象实例,但这是作弊! :) – 2010-04-07 18:52:13

+0

但是,该示例还显示了一个真实的用例。 – SLaks 2010-04-07 19:07:40

+0

它的确如此,但通过'new'表达式递归调用,而不是通过'this'直接调用构造函数 - 是的,你也是这样做的,但这不是让你的例子递归的原因。我已经更新了我的回答,以澄清这种差异。 – 2010-04-07 19:26:39

0

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

在下面的递归构造例如,我可以调用new User()new 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; 
    } 
} 

在下面的非递归构造例如,如果我没有通过一个名称构造,然后将名称设置为“新用户”。如果我不设置名称,我只想调用无参数构造函数。如果我做了一个递归调用构造在这里,我将结束设置用户名两次:

  1. 拥有多个构造
  2. 有代码:

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

    如果你只使用递归的构造函数在你的构造

  3. 想递归地使用代码,在另一个构造
+1

这不是递归,而是重用;具有不同参数的方法是不同的方法(重载),所以你没有调用相同的构造函数。你正在调用一个不同的构造函数。问题是关于调用相同的构造函数(因此以某种方式动态地决定是否这样做,以便最终终止),这是非法的。 – 2010-04-07 19:17:37

+0

@丹尼尔,我可以发誓,从“要记住的事情”,这就是问题的所在,但我们会看到该人何时回应。 – 2010-04-07 19:47:23

+0

我的意思是递归。我完全知道重用构造函数使用不太特定的构造函数来调用最多(或更多)特定的构造函数。 – Penang 2010-04-07 19:55:41

0

The return type of a constructor is void.

不,它不是。

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

号,只能调用其他的构造函数,只有当不会导致当前构造函数的递归调用。这就是为什么你会收到你提到的错误信息。

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

怎么样?你为什么想要re - 初始化一个对象?什么时候你不能一次完成?在39年的计算机编程和20年的OO中从未有过这个问题。

Would there be any benefit from allowing recursive constructors?

您还没有拿出任何...

1

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

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

其次,您可以通过将所有工作卸载到一个可以随意递增的工作方法,轻松地进行递归。

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

为什么要在构造函数体中稍后进行委托/超构造函数调用的一般原因是参数操作。你可以用(静态)辅助函数来完成这些计算并提供正确的值。这通常更清洁,但不是所有情况。实际的执行速度不应该受到影响,因为热点可以很好地将这些事物联系起来。

这意味着最后的考虑归结为提供委托/超级通话的自由放置的灵活性与通过使不正确的实践难以提供的增加的安全性。 Java设计人员(以及一般的Java理念)所做的选择是让专业人员以牺牲原始语言能力为代价来做错误的事情变得更加困难(复杂性增加)。所做的选择对我来说是一个有效的选择,虽然我个人会喜欢这种能力(可以在JVM上始终实现一个java ++语言,但没有这些限制)。

相关问题