Java - 将实例变量传递给this()方法

3

我正在学习如何使用this()来调用重载构造函数,并遇到了以下限制:

在调用this()时,您不能使用构造函数类的任何实例变量

例如:

class Test{
    int x;

    public Test() {
        this(x); //Does not compile
    }

    public Test(int y) {}

    void method1() {
        method2(x); //OK
    }

    void method2(int y) {}
}

我知道不需要将实例字段传递给构造函数,因为它默认是可见的。但是,为什么实例方法没有应用相同的限制呢?


1
简而言之:构造函数构造类。因此,假设该类内部已经存在某些内容似乎是不安全的。在这种情况下,是 x - lucidbrot
一个更有趣的测试当然是从构造函数中调用method2而不是从方法中调用。 - Maarten Bodewes
相关链接:https://dev59.com/2mct5IYBdhLWcg3wKqMd#12308755 以及:https://dev59.com/BXA65IYBdhLWcg3wrgwa - lucidbrot
https://dev59.com/gmUq5IYBdhLWcg3wF8dQ#14806340 表示实例变量的初始化发生在构造函数的开始。这使得我的第一个评论无效了 - 如果变量已经被初始化了。 - lucidbrot
3个回答

5
在Java中,还有一个要求:在任何构造函数中,必须首先执行构造函数调用(使用this())。构造函数将初始化对象。
在这些初始调用之后,实例字段才被初始化。因此,由于字段值现在已经定义完毕,您可以将它们用于任何事情,包括调用其他方法。
然而,在初始构造函数调用之前,字段处于未定义状态,不能用作其他构造函数调用的参数。
对于这类问题,您需要查看JLS:
  1. 如果此构造函数以同一类中的另一个构造函数的显式构造函数调用(§8.8.7.1)(使用this)开头,则使用同样的五个步骤评估参数并递归处理该构造函数调用。如果该构造函数调用异常完成,则出于同样的原因使此过程突然完成;否则,请继续进行第5步。

  2. 执行此类的实例初始化程序和实例变量初始化程序,将实例变量初始化程序的值分配给相应的实例变量,按照它们在类的源代码中以文本方式出现的从左到右的顺序。如果执行任何这些初始化程序导致异常,则不再处理任何其他初始化程序,并且此过程会突然完成,原因是同样的异常。否则,请继续进行第5步。

因此,在构造函数调用之后才初始化实例变量。这是有道理的,因为先给它分配默认值(零或null),然后再从构造函数内部赋给另一个值,将会很奇怪。

2
构造函数用于构造实例。因此,在构造函数开始时不应该期望实例变量x已经被初始化。
另一方面,实例方法已经可以访问实例变量。没有理由禁止将它们作为参数传递到另一个实例方法中。

然而,当我们进一步思考时,对构造函数的限制就不再那么有意义了。我们也能在那里访问实例变量,那么为什么不能将其作为参数传递给另一个构造函数呢?

因此,更好的问题是:为什么我们不能在构造函数内部将实例变量传递给调用重载构造函数的构造函数?
这个问题已经得到了很好的回答。甚至可以认为这是一个重复的问题(但由于需要理解为什么,我写了一个答案而不是简单地标记)。

在类体外部声明和初始化的实例字段,例如您的int x;,会在调用重载构造函数之后被赋值。

你可以将其与我们在调用构造函数重载方面所具有的另一个限制进行比较:我们只能在构造函数的第一行中这样做。就在开始的时候。然后,变量尚未初始化。但是它们会在第一个非构造函数调用指令之前初始化。
这里有一些与为什么存在另一个限制相关的旁观信息,可以在这里那里找到:

因为JLS(Java 语言规范)是这样规定的。JLS是否能以兼容的方式进行更改以允许它?可以。

 

历史上,在构造函数中,this()或super()必须放在首位。这个限制从来不受欢迎,被认为是武断的。有许多微妙的原因,包括验证invokespecial,导致了这个限制。多年来,我们已经在VM层面解决了这些问题,以至于可以考虑取消这个限制,不仅适用于记录,而且适用于所有构造函数。

0

您应该教授类的初始化顺序,例如:https://www.baeldung.com/java-initialization

当类构造函数被调用时,int x字段没有被初始化。您可以在构造函数中设置(初始化)`int x’,但不要调用它。

我不知道为什么您要遵循这种方式,但您可以使用static字段:

class Test{ static int x;

    public Test() {
        this(x); //Does not compile
    }

    public Test(int y) {}

    void method1() {
        method2(x); //OK
    }

    void method2(int y) {}
}

你也可以在调用的那一行初始化静态字段。

static int x =4/2;

或者在静态块中:

static int x;
static {
    x = 4/2;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接