java - 构造函数中可覆盖的方法调用有什么问题?

标签 java oop inheritance constructor overriding

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

public abstract class BasicPage extends WebPage {

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

    protected abstract String getTitle();

}

NetBeans 用“构造函数中的可覆盖方法调用”消息警告我,但它应该有什么问题?我能想象的唯一选择是将其他抽象方法的结果传递给子类中的 super 构造函数。但是使用很多参数可能很难阅读。

最佳答案

关于从构造函数调用可重写方法

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

引自Effective Java 2nd Edition,Item 17: Design and document for inheritance,或者禁止它:

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"
    }
}

这里,当Base构造函数调用overrideMe时,Child还没有完成final int x的初始化,而该方法得到错误的值。这几乎肯定会导致错误和错误。

相关问题

另见


关于多参数的对象构造

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

这里引用Effective Java 2nd Edition,Item 2:在面对许多构造函数参数时考虑构建器模式:

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...

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

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

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

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

但是,您目前不能只设置 nameisAdjustable,而将 levels 保留为默认值。您可以提供更多的构造函数重载,但显然数量会随着参数数量的增加而爆炸式增长,您甚至可能有多个 booleanint 参数,这确实会产生乱七八糟的。

如您所见,这不是一个令人愉快的编写模式,使用起来更不愉快(这里的“真”是什么意思?13 是多少?)。

Bloch 建议使用构建器模式,这样您就可以编写如下代码:

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

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

另见

相关问题

关于java - 构造函数中可覆盖的方法调用有什么问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3404301/

相关文章:

java - 使用内部类进行子类化,仅序列化外部类

Java继承问题

java - JDBC4中的驱动类是如何定位的

python - 在高级功能中访问低级功能选项的清晰方法?

java - Android 空指针异常与微调器

language-agnostic - 关系数据库中的"Diffing"对象

oop - 具有空子类的类继承

c++ - 如何选择调用的重载函数

java - 雅各布 : calling vb function in Excel file without invoking "Open" statement

Java - 确切含义http.maxConnections