实例变量初始化问题
这是一些示例代码,
class Base
{
private int val;
Base() {
val = lookup();
}
public int lookup() {
//Perform some lookup
// int num = someLookup();
return 5;
}
public int value() {
return val;
}
}
class Derived extends Base
{
private int num = 10;
public int lookup() {
return num;
}
}
class Test
{
public static void main(String args[]) {
Derived d = new Derived();
System.out.println("d.value() returns " + d.value());
}
}
输出:d.value() 返回 0 // 我期望 10,因为 Lookup() 被覆盖,但不是 0!有人可以澄清一下吗?
Derived
的实例变量的初始化在其查找方法执行时尚未发生。如何确保 Derived
的实例变量在调用其方法时已初始化?
Heres some sample code,
class Base
{
private int val;
Base() {
val = lookup();
}
public int lookup() {
//Perform some lookup
// int num = someLookup();
return 5;
}
public int value() {
return val;
}
}
class Derived extends Base
{
private int num = 10;
public int lookup() {
return num;
}
}
class Test
{
public static void main(String args[]) {
Derived d = new Derived();
System.out.println("d.value() returns " + d.value());
}
}
output: d.value() returns 0 // I expected 10 as lookup() is overridden, but not 0! can someone clarify this?
The initialization of Derived
's instance variables has not happened at the time its lookup method executes. How do I make sure the instance variables of Derived
are initialized when its method is called?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
首先,由于缺少
someLookup
方法,该代码无法编译。无论如何,除此之外,我相信您的问题是您的期望是无效的,因为构造函数是分层运行的。
超类的构造函数始终在子类的构造函数之前运行,这包括子类变量的初始值设定项(它们实际上作为构造函数的一部分运行)。因此,当您创建
Derived
实例时,会发生以下情况:Base
构造函数。lookup()
,它使用Derived
中的实现。num
,这是此时的默认值,因为 Derived 的构造函数和初始值设定项尚未运行。val
设置为 0。Derived
初始化程序和构造函数正在运行 - 从 this 点调用lookup
将返回10.一般来说,正是出于这个原因,从构造函数中调用非最终方法是一个坏主意,并且许多静态分析工具会警告您不要这样做。这类似于在构造过程中让对象引用泄漏,您最终可能会得到一个使类级不变量无效的实例(在您的情况下,Derived 的
num
总是“10”,但它可以被视为 0在某些时候)。编辑:请注意,对于这种特定情况,无需任何额外的代码,您可以通过将
num
设置为常量来解决问题:这实际上会做您想要的事情,因为静态初始化程序在类加载时运行(必须在调用构造函数之前发生)。然而,这确实假设以下情况是可以的:
a)类的所有实例共享相同的
num
变量;b)
num
永远不需要改变(如果这是真的,那么(a)自动为真)。在您给出的确切代码中,情况显然如此,但我希望您可能会为了简洁而省略额外的功能。
我将其包含在这里是为了进行比较和兴趣,而不是因为它是一般意义上的这个“问题”的解决方法(因为它不是)。
Well for a start, that code doesn't compile due to the lack of
someLookup
method.Anyway, asides from that I believe your issue is that your expections are invalid because of the way constructors are run hierarchically.
A superclass' constructor is always run before the subclass', and this includes initializers for the subclass' variables (which are really run as part of the constructor). So, when you create your instance of
Derived
, the following happens:Base
constructor is invoked first.lookup()
is called, which uses the implementation inDerived
.num
is returned, which is the default value at this point because Derived's constructor and initializers have not been run.val
is set to 0.Derived
initializers and constructor are run - callinglookup
from this point on will return 10.In general, it's a bad idea to call a non-final method from a constructor for exactly this reason, and many static analysis tools will warn you against it. It's similar to letting object references leak during construction, you can end up with an instance that invalidates class-level invariants (in your case, Derived's
num
is "always" 10 yet it can be seen to be 0 at some points).Edit: Note that for this particular case, without any additional code, you could resolve the issue by making
num
a constant:This would actually do what you want, because the static initializer is run when the class is loaded (which has to happen before the constructors are called). This does however assume that it's fine for:
a) all instances of the class to share the same
num
variable;b)
num
never needs to change (if this is true then (a) is true automatically).In the exact code you've given this is clearly the case, but I expect you may be omitting extra functionality for brevity.
I include this here for comparison and interest, not because it's a workaround to this "issue" in a general sense (because it's not).
返回 0 的原因是在将 10 分配给 Derived 中的 num 之前,正在调用构造函数 Base(并在 Derived 中调用查找)。
一般来说,在初始化派生实例字段之前调用基本构造函数。
The reason you are getting 0 returned is that the constructors Base is being called (and calling lookup in Derived) before 10 is assigned to num in Derived.
To put generally, the base constructor is called before the derived instance fields are initialised.
对于为什么在构造基类时无法访问子类字段已经有很多很好的答案,但我认为您要求如何:一个可行的解决方案像这样的事情:
实际的方法是在基类上引入 init() 方法,然后从子类的构造函数中调用它,例如:
唯一的问题是,你不能强制子类调用 init() 方法code> 基类上的方法,并且基类可能未正确初始化。但是通过标志和一些例外,我们可以在运行时提醒程序员他应该调用
init()
...There are a lot of great answers already on why you can't access subclass fields while constructing the base class, but I think you asked for a how: a working solution for something like this:
The practical way is to introduce an init() method on the base class an call it from the subclass's constructor, like:
Only problem is, you can't force subclasses to call the
init()
method on the base class and the base class might not be properly initialized. But with a flag and some exceptions we can remind the programmer at runtime that he should have calledinit()
...在构造函数中调用可以在子类中重写的方法通常是一个坏主意。在您的示例中会发生以下情况:
由于基构造函数调用lookup时子类构造函数尚未完成,因此对象尚未完全初始化并且lookup返回默认值num 字段的值。
It is generally a bad idea to call methods in a constructor that can be overriden in a subclass. In your example the following happens:
Since the subclass constructor is not finished when the base constructor calls lookup, the object is not yet completely initialized and lookup returns the default value of the num field.
让我们慢慢看:
第 1 步,您在 Derived 上调用(默认)构造函数,在设置 num = 10 之前,它链接到 Base 的构造函数,该构造函数调用 Derived 的查找方法,但 num 尚未设置,因此 val 仍未初始化。
步骤2,调用属于Base的d.value(),并且val由于1而未设置,因此得到0而不是10。
Let's take it slowly:
Step 1, you call the (default) constructor on Derived, before setting num = 10, it chains up to Base's constructor, which calls Derived's lookup method, but num has not been set, so val remains uninitialized.
Step 2, you call d.value(), which belongs to Base, and val is unset due to 1, and therefore you get 0 instead of 10.
您已重写
Derived
类中的lookup()
方法,因此当调用Base
构造函数时,它会调用Derived< 中的方法/code> 其中主体是
return num
。在Base
初始化时,Derived
的num
实例变量尚未初始化并且为 0。这就是为什么 val 被分配为 0基础
。如果我正确理解您的意图,您应该将
Base
中的value
方法更改为:You have overriden method
lookup()
in theDerived
class, so when theBase
constructor is called it calls the method fromDerived
which body isreturn num
. At the time ofBase
initialization thenum
instance variable of theDerived
is not yet initialized and is 0. That's why val is assigned to 0 inBase
.If I understood your intentions correctly, you should change the
value
method inBase
to be:当构造函数调用此代码时,下面的代码将返回 0(通过查看程序,您会期望返回 10)。原因很简单,num还没有初始化,父类调用了这个方法。
The below piece of code is returing 0 (you would expect 10 by looking at the program) when the constructor makes a call to this. The simple reason is that num is not initialized yet and the parent class calls this method.