在Java中,是否有合理的理由从类构造函数中调用非final方法? [英] In Java, is there a legitimate reason to call a non-final method from a class constructor?

查看:120
本文介绍了在Java中,是否有合理的理由从类构造函数中调用非final方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近花了几分钟调试生产代码中的问题,最终结果是由在其构造函数中调用抽象方法的类引起的,并且该方法的子类实现尝试使用子类字段还没有被初始化(一个例子说明了这一点,包括在下面)

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:


一般来说,在构造函数中调用非final方法是个坏主意,正是因为这个原因 - 子类构造函数体还没有被执行,所以你有效地调用了一个方法。尚未完全初始化的环境。

In general it's a bad idea to call a non-final method within a constructor for precisely this reason - the subclass constructor body won't have been executed yet, so you're effectively calling a method in an environment which hasn't been fully initialized.

这让我有些疑惑,是否有合理的理由要求非决赛或抽象的方法d来自构造函数?或者它几乎总是设计糟糕的迹象?

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();
    }
}

这是预期的输出:


在method()中:null

在B()中:[foo,bar]

在方法中( ):[foo,bar]

In method(): null
In B(): [foo, bar]
In method(): [foo, bar]

问题当然是在第一次调用方法时()字段 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.

它们无法传递给构造函数链,因为某些字段需要引用回创建它们的时间顺序,以后 - 而且你不能使用这个在一个链式构造函数参数中。

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中,是否有合理的理由从类构造函数中调用非final方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆