我们通过将大部分构造函数代码移到onInitialize()
来避免此PMD警告。但我们是否只是将问题(设计缺陷?)转移到一个不同的地方?
即。我们的onInitialize()
只是一个代理构造函数,PMD没有注意到它吗?
当你在构造函数中调用可覆盖的方法时,我们遇到了弹出的类型的问题,但这似乎源于Wicket本身调用了一个(无法找到确切的源代码行,但是{{ 1}},一个可覆盖的方法,当你在构造函数中调用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()
第
您可能遇到的唯一警告是NullPointerException
的JavaDoc:
注意:此调用的时间不准确,合同是在{@link Component#onBeforeRender()}之前的某个时间调用。
答案 0 :(得分:4)
如果仅从onInitialized()
方法内部向容器添加组件,则不会出现此问题。但它无法通过PMD验证,至少不能通过内置规则进行验证。
但我认为这不是设计缺陷。这是一个设计决定。您不能将所有设计都基于静态分析工具和预定义规则。 API可用性也是设计的一个重要方面,有时甚至比设计原则更具相关性。
例如,CQS (Command-Query Separation)原则要求做某事的方法(更改状态)不应该返回任何内容,并且返回某些东西的方法不应该没有副作用(改变状态)。
如果这是一个硬规则,则无法实现流畅的接口(更改对象状态的方法,并返回this
,允许方法链接)。 Wicket广泛使用它(几乎所有组件操作方法都返回this
),这是使用它的乐趣之一。
PMD是一个非常有用的工具。但你必须是工具的主人,而不是它的奴隶。您应该将其警告视为可能的问题,但如果您对自己的设计选择有信心,只需将代码标记为绕过并感到高兴。
答案 1 :(得分: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
乍一看也很好 - 它会从i
返回val()
,而null
永远不会final
,因为它的B
初始化为NullPointerException
构造函数。但是,创建super()
实例将抛出i
。你能明白为什么吗? 提示:查看隐式private final Integer i = 1;
发生的位置。
您认为移动onInitialize()
初始化会有所帮助吗?为什么不呢?
{{1}}
根据经验,永远不要从非final类的构造函数中调用非私有方法。事实上,在这种情况下我甚至会触发编译错误。正如您所看到的,此问题与Wicket无关,将初始化移至{{1}}是为了避免此类陷阱。
答案 2 :(得分:0)
我们遇到了在构造函数中调用可覆盖方法时弹出的排序问题,但这似乎源于Wicket本身调用一个(无法找到确切的源代码行,但是onInitialize(),一个可覆盖的方法,当你在构造函数中调用add()时,最终会被调用。)
我很确定调用onInitialize()
时调用的add(component)
方法是要添加的组件的onInitialize()
方法(add方法的参数)而不是你正在修建的课程。这应该没问题,因为该组件已经完全构建。