【问题标题】:Java overridable call in constructor构造函数中的 Java 可覆盖调用
【发布时间】:2011-08-31 13:09:04
【问题描述】:

我知道从 Java 中的对象构造函数调用可覆盖的方法是一种不好的(安全)做法。但是,例如,如果构造函数必须初始化一些数据,调用相应的 setter 方法似乎是合理的,这样我就不会复制代码。设置器是公开的,而不是最终的。是否有任何标准的方法来处理这个问题,比如声明私有的 setter 方法,公共的调用?为了说明,这里有一些代码:

class A {
    private double x,y;
    private privateSetX(double x1) { x=x1; }
    private privateSetY(double y1) { y=y1; }
    public A() { privateSetX(0); privateSetY(0); }
    public setX(double x1) { privateSetX(x1); }
    public setY(double y1) { privateSetY(y1); }
};

【问题讨论】:

  • 大概在这种情况下,您无论如何都不想覆盖设置器?在这种情况下,它们应该被声明为final
  • Oli 真的一针见血。它引出了一个问题,即您是否应该拥有公共设置器,但只有私有设置器并具有公共的外部交互行为(你知道,当 A 类去和“A-ify things”或其他任何事情时)调用二传手。
  • @Oli 好吧,所以碰巧这个类足够通用,我希望它可以被子类化并且方法 - 可覆盖,所以我没有制作 setter @987654324 @

标签: java methods constructor overriding encapsulation


【解决方案1】:

创建需要在构造过程中设置许多不同字段的对象的更好方法是使用Builder Pattern

我不会重复其他人的努力,我只会为您指出有关此主题的most excellent SO answer

如果问题是您需要在构造函数期间覆盖 setter,您可以创建一个 Builders 层次结构,而不是创建您尝试构建的主类的层次结构,或者除此之外。

【讨论】:

    【解决方案2】:

    如果您真的想要这样做,请创建一个由构造函数和公共 setter 调用的辅助私有 setter 方法。

    【讨论】:

    • 哦,这样做的缺点之一是您不能拥有 final 实例字段。
    • @Oli:是的,就像在给定的 sn-p 中一样。只是确认。但是,我不建议将此作为最佳实践。
    • @NathanRyan - 很难想象final 实例字段也会有一个设置器。 :)
    • @TedHopp 这在一定程度上取决于您的编码标准。使用 final 实例字段,编译器无法确定明确的赋值,因此 setter 中的赋值语句将被标记为编译器错误。但是我已经看到了要求通过设置器初始化字段以启用注入和/或检测的编码标准,尽管事实上这些字段在实践中是最终的。这些编码标准是否合适是另一回事:-)
    • final 字段(使用关键字声明的字段,这就是我所说的)和“final in practice”字段之间存在很大差异。如果编码标准要求通过设置器初始化字段,则该字段根本不能声明为final。同样(正如您所指出的),编译器不允许声明 final 的字段也有一个设置器,这是无法避免的。当然,使用构建器模式(如@shoover 的回答),可以同时拥有:final 字段,通过设置器初始化(然后将在构建器类中)。
    【解决方案3】:

    我认为在构造函数中直接初始化数据成员是更好的做法。如果您调用一个方法,那么您必须查看该方法的实现,以验证它确实在做它看起来正在做的事情。如果您直接分配给数据成员,您知道正在初始化。所以在你的代码中:

    class A {
        private double x, y;
        public A() {
            x = 0;
            y = 0;
        }
        // ...
    }
    

    构造函数通常应该简单、确定且明显正确。直接分配可以满足这些目标。

    【讨论】:

    • 对我来说似乎也不是很干燥。 Setter 可以做一些事情,比如清理输入等。我们需要在构造函数中做同样的事情。
    • 对复制 single 赋值语句有什么反感?构造函数中的赋值是initialisation,而setter方法中的赋值是改变一个现有的值。它们是不同的目的。
    • @K.Steff:作为更好的做法,您可能想要做一些事情:将单独的赋值留在构造函数和设置器中,而不是提取中间计算(如复杂的参数验证) 作为单独的私有方法。
    • @elusive 清理包私有静态方法中的输入,这些方法已经过单元测试并且没有副作用。从构造函数和访问器/修改器调用这些方法。
    猜你喜欢
    • 2013-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-09
    • 2017-02-03
    • 2011-03-25
    相关资源
    最近更新 更多