2

我们通过将大部分构造函数代码移动到onInitialize(). 但是我们只是将问题(设计缺陷?)转移到不同的地方吗?

即我们onInitialize()只是一个代理构造函数,PMD 没有注意到吗?

当您在构造函数中调用可覆盖的方法时,我们遇到了那种弹出的问题,但这似乎源于 Wicket 本身调用一个(找不到确切的源代码行,但是onInitialize(),一个可覆盖的方法,最终当你调用add()构造函数时被调用)。

如果有帮助,很高兴提供示例代码。

public class PageA extends WebPage {

    protected SomeBean bean;

    public PageA() {
        add(new Label("foo", "bar"));
        bean = new SomeBean();
    }

}

public class PageB extends PageA {

    public PageB() {
        super();
    }

    @Override
    protected void onInitialize() {
        add(new Label("rofl", bean.getSomeText()));
    }
}

您会认为这很好,但调用onInitialize不会发生在您认为会发生的地方:

在页面上调用add()时,方法流程是:

MarkupContainer    add()    
MarkupContainer    addedComponent() 
Page               componentAdded()
MarkupContainer    initialize()
Component          fireInitialize()
Component          onInitialize()

因此,您可以看到是否将组件添加到WebPage方法onInitialize()被触发,这是一个可覆盖的方法,导致上述看起来理智的代码实例创建NullPointerExceptions.

您得到的唯一警告可能会发生这种情况是 JavaDoc 的onInitialize()

注意:此调用的时间不准确,约定是在 {@link Component#onBeforeRender()} 之前的某个时间调用它。

4

3 回答 3

4

如果您只从容器的onInitialized()方法内部将组件添加到容器,则不会出现此问题。但它不能被 PMD 验证,至少不能被内置规则验证。

不过,我不认为这是设计缺陷。这是一个设计决定。您不能将所有设计都基于静态分析工具和预定义规则。API 可用性也是设计的一个重要方面,有时甚至比设计原则更相关。

例如,CQS(命令-查询分离)原则将要求做某事(更改状态)的方法不应返回任何内容,返回某事的方法不应有任何副作用(更改状态)。

this如果这是一个硬性规则,则无法实现流畅的接口(改变对象状态的方法,并返回,允许方法链接)。Wicket 广泛使用它(几乎所有组件操作方法都返回this),这是使它使用起来很有趣的原因之一。

PMD 是一个非常有用的工具。但你必须是工具的主人,而不是它的奴隶。您应该将其警告视为可能的问题,但如果您对自己的设计选择有信心,只需将代码标记为要绕过并感到高兴。

于 2011-06-09T12:16:11.173 回答
1

考虑以下类:

public class A {

    public A() {
        System.out.println(val().toString());
    }

    protected Integer val() {
        return 0;
    }

}

乍一看还不错,假设val()不应该返回null,就是这样。现在B子类A

public class B extends A {

    private final Integer i;

    public B() {
        //super();    //implicit
        i = 1;
    }

    @Override
    protected Integer val() {
        return i;
    }
}

B乍一看也很好 - 它ival()哪个返回,null因为它final是在构造函数中初始化的。但是创建B实例会抛出NullPointerException. 你能看出为什么吗?提示:查看隐式super()发生的位置。

你认为移动i初始化会有帮助吗?为什么不?

private final Integer i = 1;

根据经验,永远不要从非最终类的构造函数中调用非私有方法。事实上,在这种情况下,我什至会触发编译错误。如您所见,此问题与 Wicket 无关,将初始化移动到onInitialize()是为了避免此类陷阱。

于 2011-06-09T11:12:11.940 回答
0

当您在构造函数中调用可重写方法时,我们遇到了那种弹出的问题,但这似乎源于 Wicket 本身调用一个(找不到确切的源代码行,但 onInitialize(),一种可重写方法, 当您在构造函数中调用 add() 时,最终会被调用)。

我很确定onInitialize()您调用时调用add(component)onInitialize()方法是正在添加的组件的方法(add 方法的参数),而不是您当前正在构建的类的方法。这应该没问题,因为该组件已经完全构建。

于 2011-06-09T11:45:20.250 回答