如果类
T
的构造函数将 this
作为参数传递给某个方法 foo
,并且 foo
立即对 T
对象(尚未完成执行其构造函数)执行回调,事情显然可能会出错,但是如果我确保 foo
访问的任何字段都已初始化怎么办?
为了清楚地说明我在说什么,这里有一个确实出错的例子:
public class T {
// Class that accesses T, which could be defined somewhere outside
// rather than nested here.
static class FortyTwoChecker {
void foo(T t) {
System.out.println("Fourty-two is: "+t.get42());
}
}
// Fields set in constructor.
private final int fortyTwo;
// Callback method that accesses (and might also modify, in some other case)
// fields set in constructor.
int get42() { return fortyTwo; }
T(FortyTwoChecker checker) {
checker.foo(this); // goes wrong!
fortyTwo = 42;
}
}
当构造
T
对象时,会打印 Fourty-two is: 0
,因为 foo
在设置 fortyTwo
之前被调用。但如果我交换 T
构造函数中的两个语句,输出将是预期的 Fourty-two is: 42
。我的问题是:我能确定这种代码总是按预期工作吗?也就是说,是否可以保证编译器/VM 不会扰乱顺序执行顺序,并且不会仅仅因为调用了不完整构造对象的方法而出现错误?
我预计如果回调是从另一个线程进行的,这可能很难回答,但如果所有事情都发生在同一个线程中,我至少可以确定吗?
(显然,最好确保在
T
对象完全构造完成之前不进行回调——并且不必担心——但在让我思考这一点的用例中,这会引发其他问题。 )
你的错误在于用对象来思考,当你必须用属性来思考时,让我们举个例子来说明为什么会这样:
class Cat {
int age;
String name;
public Cat() {}
// setters and getters
}
class Main {
public Main() {
Cat black = new Cat();
black.setAge( 10 );
Cat white = new Cat();
white.setAge( black.getAge() ); // work!
white.setName( black.getName() ); // not work!
}
}
正如您在示例中看到的,每个属性的使用都是独立的,我可以实例化“age”并使用它,而无需实例化“name”,因此该语言无法制定如何使用它的规则实例化一个对象。