在构造函数中的可重写方法调用有什么问题? [英] What's wrong with overridable method calls in constructors?

查看:322
本文介绍了在构造函数中的可重写方法调用有什么问题?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Wicket页面类,根据抽象方法的结果设置页面标题。

I have a Wicket page class that sets the page title depending on the result of an abstract method.

public abstract class BasicPage extends WebPage {

        public BasicPage() {
                add(new Label("title", getTitle()));
        }

        protected abstract String getTitle();

}

NetBeans警告我构造函数中的可覆盖方法调用,但是应该是什么错误呢?唯一的选择我可以想象是将其他抽象方法的结果传递给子类的超级构造函数。

NetBeans warns me with the message "Overridable method call in constructor", but what should be wrong with it? The only alternative I can imagine is to pass the results of otherwise abstract methods to the super constructor in subclasses. But that could be hard to read with many parameters.

推荐答案

从构造函数调用可覆盖方法



简单地说,这是错误的,因为它不必要地打开了多个错误的可能性。当调用 @Override 时,对象的状态可能不一致和/或不完整。

On invoking overridable method from constructors

Simply put, this is wrong because it unnecessarily opens up possibilities to MANY bugs. When the @Override is invoked, the state of the object may be inconsistent and/or incomplete.

from Effective Java 2nd Edition,Item 17:Design and document for inheritance,or else prohibit it

A quote from Effective Java 2nd Edition, Item 17: Design and document for inheritance, or else prohibit it:


是一个类必须遵守允许继承的几个限制。 构造函数不得直接或间接调用可覆盖的方法。如果违反此规则,将导致程序失败。超类构造函数在子类构造函数之前运行,因此子类中的重写方法将在子类构造函数运行之前被调用。如果覆盖方法取决于子类构造函数执行的任何初始化,则该方法将不会按预期方式运行。

There are a few more restrictions that a class must obey to allow inheritance. Constructors must not invoke overridable methods, directly or indirectly. If you violate this rule, program failure will result. The superclass constructor runs before the subclass constructor, so the overriding method in the subclass will be invoked before the subclass constructor has run. If the overriding method depends on any initialization performed by the subclass constructor, the method will not behave as expected.

public class ConstructorCallsOverride {
    public static void main(String[] args) {
        abstract class Base {
            Base() { overrideMe(); }
            abstract void overrideMe(); 
        }
        class Child extends Base {
            final int x;
            Child(int x) { this.x = x; }
            @Override void overrideMe() {
                System.out.println(x);
            }
        }
        new Child(42); // prints "0"
    }
}

c $ c> Base 构造函数调用 overrideMe 尚未完成初始化 final int x ,并且该方法获取错误的值。

Here, when Base constructor calls overrideMe, Child has not finished initializing the final int x, and the method gets the wrong value. This will almost certainly lead to bugs and errors.

  • Calling an Overridden Method from a Parent-Class Constructor
  • State of Derived class object when Base class constructor calls overridden method in Java
  • Using abstract init() function in abstract class’s constructor
  • FindBugs - Uninitialized read of field method called from constructor of superclass

具有许多参数的构造函数可能导致可读性差,并且存在更好的替代方案。

Constructors with many parameters can lead to poor readability, and better alternatives exist.

以下是 Effective Java第2版的引用:第2项:面对许多构造函数参数时考虑一个构建器模式:

Here's a quote from Effective Java 2nd Edition, Item 2: Consider a builder pattern when faced with many constructor parameters:


传统上,程序员使用了伸缩构造函数模式,其中只提供了必需的参数,另一个带有一个可选参数,第三个带有两个可选参数,等等...

Traditionally, programmers have used the telescoping constructor pattern, in which you provide a constructor with only the required parameters, another with a single optional parameters, a third with two optional parameters, and so on...

伸缩构造函数模式本质上是这样的:

The telescoping constructor pattern is essentially something like this:

public class Telescope {
    final String name;
    final int levels;
    final boolean isAdjustable;

    public Telescope(String name) {
        this(name, 5);
    }
    public Telescope(String name, int levels) {
        this(name, levels, false);
    }
    public Telescope(String name, int levels, boolean isAdjustable) {       
        this.name = name;
        this.levels = levels;
        this.isAdjustable = isAdjustable;
    }
}

现在您可以执行以下任何操作: / p>

And now you can do any of the following:

new Telescope("X/1999");
new Telescope("X/1999", 13);
new Telescope("X/1999", 13, true);

但是,目前不能只设置名称 isAdjustable ,并在默认情况下保留级别。你可以提供更多的构造函数重载,但是显然数量会随着参数数量的增长而爆炸,你甚至可能有多个 boolean int 参数,这将真的使一些乱七八糟的东西。

You can't, however, currently set only the name and isAdjustable, and leaving levels at default. You can provide more constructor overloads, but obviously the number would explode as the number of parameters grow, and you may even have multiple boolean and int arguments, which would really make a mess out of things.

正如你可以看到,这不是一个令人愉快的模式写,甚至更少使用愉快(这里的真是什么意思?13是什么?)。

As you can see, this isn't a pleasant pattern to write, and even less pleasant to use (What does "true" mean here? What's 13?).

Bloch建议使用构建器模式,

Bloch recommends using a builder pattern, which would allow you to write something like this instead:

Telescope telly = new Telescope.Builder("X/1999").setAdjustable(true).build();

请注意,现在参数已命名,您可以按任何顺序设置它们,可以跳过要保留的默认值。这确实比伸缩式构造函数好得多,特别是当有大量的参数属于许多相同类型时。

Note that now the parameters are named, and you can set them in any order you want, and you can skip the ones that you want to keep at default values. This is certainly much better than telescoping constructors, especially when there's a huge number of parameters that belong to many of the same types.

  • Wikipedia/Builder pattern
  • Effective Java 2nd Edition, Item 2: Consider a builder pattern when faced with many constructor parameters (excerpt online)
  • When would you use the Builder Pattern?
  • Is this a well known design pattern? What is its name?

这篇关于在构造函数中的可重写方法调用有什么问题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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