问题描述
我最近花了好几分钟调试生产代码中的一个问题,最终证明是一个类在其构造函数中调用了一个抽象方法,而该方法的子类实现试图使用一个子类字段尚未初始化(下面包含一个说明这一点的示例)
I recently spent quite a few minutes debugging a problem in production code that in the end turned out to be caused by a class calling an abstract method in its constructor, and the subclass implementation of that method tried to use a subclass field that had not been initialized yet (An example that illustrates the point is included below)
在研究这个时,我偶然发现了这个问题,并且对 Jon Skeet 的回答很感兴趣:
While researching this, I stumbled across this question, and was intrigued by Jon Skeet's answer:
一般来说,正是出于这个原因,在构造函数中调用非最终方法是一个坏主意——子类构造函数体还没有被执行,所以你在一个没有执行的环境中有效地调用了一个方法t 已完全初始化.
这让我想知道,是否有正当理由从构造函数调用非最终方法或抽象方法?或者它几乎总是一个糟糕设计的迹象?
This has me wondering, is there ever a legitimate reason to call a non-final or abstract method from a constructor? Or is it pretty much always a sign of bad design?
public class SSCCE {
static abstract class A {
public A() {
method(); // Not good; field arr in B will be null at this point!
}
abstract void method();
}
static class B extends A {
final String[] arr = new String[] { "foo", "bar" };
public B() {
super();
System.out.println("In B(): " + Arrays.toString(arr));
}
void method() {
System.out.println("In method(): " + Arrays.toString(arr));
}
}
public static void main(String[] args) {
new B().method();
}
}
这里是预期的输出:
在方法()中:空
在 B(): [foo, bar]
在method()中:[foo, bar]
当然,问题是在第一次调用 method()
时,字段 arr
为空,因为它还没有被初始化.
The problem, of course, is that in the first call to method()
the field arr
is null because it hasn't been initialized yet.
推荐答案
有时很难不这样做.
以 Joda Time 为例.它的 Chronology
类型层次结构非常深,但是抽象的 AssembledChronology
类基于您组装一堆字段"(月份等)的想法.有一个非最终方法,assembleFields
,它在构造函数期间被调用,以便为该实例组装字段.
Take Joda Time, for example. Its Chronology
type hierarchy is very deep, but the abstract AssembledChronology
class is based on the idea that you assemble a bunch of "fields" (month-of-year etc). There's a non-final method, assembleFields
, which is called during the constructor, in order to assemble the fields for that instance.
它们不能在构造函数链中向上传递,因为某些字段需要在稍后引用创建它们的年表 - 并且您不能在链式中使用 this
构造函数参数.
They can't be passed up the constructor chain, because some of the fields need to refer back to the chronology which creates them, later on - and you can't use this
in a chained constructor argument.
我在 Noda Time 中做了大量的工作,以避免它实际上是一个虚拟方法调用 - 但它是老实说,一些非常相似的东西.
I've gone to nasty lengths in Noda Time to avoid it actually being a virtual method call - but it's something remarkably similar, to be honest.
如果可能的话,避免这种事情是个好主意……但有时这样做会让人头疼,尤其是如果您希望您的类型在之后保持不变施工.
It's a good idea to avoid this sort of thing if you possibly can... but sometimes it's a real pain in the neck to do so, especially if you want your type to be immutable after construction.
这篇关于在 Java 中,是否有正当理由从类构造函数调用非最终方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!