在从类构造函数调用的方法中初始化最终变量

时间:2018-03-12 13:20:06

标签: java constructor initialization

今天我面临一种奇怪的行为,我无法弄明白为什么。

想象一下,我们在Java中的典型类中有一个final变量。我们可以立即或在类构造函数中初始化它:

public class MyClass {

    private final int foo;

    public MyClass() {
        foo = 0;
    }
}

但我不知道为什么我们不能在构造函数中调用方法并在该方法中初始化foo,如下所示:

public class MyClass {

    private final int foo;

    public MyClass() {
        bar();
    }

    void bar(){
        foo = 0;
    }
}

因为我认为我们仍处于构造函数流程中,但尚未完成。任何暗示都将受到热烈欢迎。

3 个答案:

答案 0 :(得分:9)

首先,在声明时将值 复制 分配给编译器的每个构造函数。其次,可以使用方法来初始化值,但您需要return才能使其正常工作。正如其他人所说,您需要确保将此值设置为一次

public class MyClass {
    private final int foo = bar();

    private static int bar() {
        return 0;
    }
}

相当于

public class MyClass {
    private final int foo;

    public MyClass() {
        this.foo = bar();
    }

    private static int bar() {
        return 0;
    }
}

请注意barstatic,否则您需要实例来调用它。

答案 1 :(得分:2)

您只能初始化一次最终变量。最终变量有三种形式:

  • 类最终变量
  • 实例最终变量
  • 本地最终变量。

对于类的最终变量,可以在声明或静态初始化程序中初始化变量:

class Program {
static final int i1 = 10;
static final int i2;
static {
    i2 = 10;
}

}

例如,最终变量,变量可以在声明,实例初始化器或构造函数中初始化:

class Program {
final int i1 = 10;
final int i2;
final int i3;
{
    i2 = 10;
}

Program() {
    i3 = 10;
}

}

对于局部最终变量,变量可以在声明中声明或在声明后的任何位置初始化。必须在使用本地最终变量之前对其进行初始化。

class Program {
void method() {
     final int i1 = 10;
     final int i2;
     System.out.println(i1);
     i2 = 10;
     System.out.println(i2);
     return ;
}

}

来源:参考链接Reference Link

答案 2 :(得分:0)

字段(或变量)上的最终修饰符意味着编译器将确保满足以下两个条件:

  • 除非对象构造失败,否则在对象构造期间字段至少初始化一次。
  • 该字段最多初始化一次。

对于您的代码,这些都不能保证:

  • 某些子类可能会覆盖方法栏。
  • 同一个包中的其他一些类可能再次调用方法栏。

使用私有方法而不是package-private可能很诱人。虽然它可以保证你们两个条件(除非你试图通过反射打破它),javac仍然不会接受它,因为它不是那么强大。有一些很好的理由:

  1. 首先,它必须有一些限制。如果编译器能够完全确定是否满足两个条件,则可以决定停止问题,这是不可能的。因此,选择了一些合理的子集。
  2. 想象一下,它能够检测到这种特殊情况。这意味着你有一些必须从构造函数而不是从其他地方调用的私有方法。在这种情况下,程序员需要一个描述性的错误消息,为什么这里不能调用这样的私有方法(看起来像一般的常规方法)。之后,有人会创建一些有条件地分配给最终场的怪物方法。对于一些复杂的条件,javac将无法确定它是否分配给最终字段,因此有人会面临一些神秘的错误消息。我不认为在这种情况下制作好的错误信息很容易。
  3. 调用私有实例方法已经非常棘手了。由于该方法将在未完全初始化的对象上运行,因此它可以读取一些未初始化的(甚至是最终的)属性。
  4. 我认为构造函数应该简短而简单,通常只是为字段分配参数,再加上一些验证。如果事情变得复杂,您可能想要创建工厂方法。对象创建将与对象方法明确分离,而当您需要调用私有方法时则不然。
  5. 语言设计师决定只支持一些易于理解的案例,而不是这种鼠标捕捉。在其他情况下,代码可能可能被重构。因此,语言创作者可以专注于一些更重要的方面。