标记为@NotNull的最终字段未初始化。

10

我有这段代码:

public static class MyWebDriver extends RemoteWebDriver {
    @NotNull
    private final String nodeId;

    public MyRemoteWebDriver(@NotNull String nodeId) {
        super();
        this.nodeId = nodeId;
    }

    @Override
    public void quit() {
        System.out.println("deleting node: " + nodeId);
    }
}

保证传入构造函数的nodeId不为null,并且由于nodeId字段是final,我期望在我的quit()方法中对其进行初始化。

但是,在super()构造函数中有一个try-catch块,如果出现异常,则调用quit()方法并抛出异常。 在这种情况下,我在quit()方法中获取的nodeId未被初始化(具有null值)。

除了super()调用之前手动初始化nodeId,还有其他避免这种情况的方法吗?

@Override
public void quit() {
    if (nodeId != null) {
        System.out.println("deleting node: " + nodeId);
    }
}

这个?看起来相当愚蠢,因为nodeId被标记为@NotNull


我只是使用了 System.out.println() 而不是依赖于 nodeId 值的实际代码。这就是我想要避免的原因。 - esin88
只是为了澄清:RemoteWebDriver类不是我的,它来自于org.openqa.selenium.remote包。 - esin88
1
@esin88,所以openqa项目(不管它是什么)在构造函数中调用了一个非final方法?看起来这应该是一个bug声明,显然目前这并没有让你的生活变得更轻松。 - Eugene
@esin88,你不会得到比你已经做的更好和更简单的答案(通过if检查)...我建议你坚持这个方法,但可以打开一个bug并添加代码注释。 - Eugene
4个回答

4
但是,在super()构造函数中有一个try-catch块,如果出现异常,则调用quit()。
这里有两个问题:
1. 构造函数除了将给定参数存储在(final)成员变量中之外,不应该做任何工作。
2. 构造函数不应该直接或间接地调用除private和/或final方法(在类内部)之外的任何其他方法(即您不能调用最终方法,后者又调用非最终方法)。
你遇到这个问题的原因是违反了单一职责模式和关注点分离原则。
无论您在超类的构造函数中做什么,都应该在一个单独的类中完成,只有该过程的结果应该传递到您的类(及其超类)中。
显然,quit()方法也属于不同的类。

1
构造函数除了将给定参数存储在(final)成员变量中,不应该做任何工作。这是否意味着类不应该验证它们的输入?我知道很多人会反对所有对象可能无效而不是根本不存在。 - chris
@chris "这意味着类不应该验证它们的输入吗?" 这取决于您要验证什么。在构造函数中,您应该只进行可以在不访问任何依赖项的情况下完成的验证。任何其他验证都应在将输入传递给构造函数之前完成。 - Timothy Truckle
4
构造函数不应直接或间接地调用非final方法,非私有性并不是问题,而是方法被覆盖的潜在可能性。 - Andy Turner
@AndyTurner "非私有性不是问题,问题在于该方法被覆盖的潜在可能性。" 感谢您指出这一点,我正在更改我的答案。 - Timothy Truckle
1
@AndyTurner 感谢您改进我的答案。 - Timothy Truckle
显示剩余3条评论

2
这当然可以通过Java的工作原理来解释。 类型为MyWebDriver的对象未被初始化,包括其字段,即在您的情况下包括nodeId,直到从超类继承的字段被初始化,即在您的情况下,直到super()返回。
因此,如果super抛出异常,显然nodeId将是null
我认为除非您使用的框架(您没有指定)提供某些解决方法,否则不会有任何解决方案(除了一些类似于您提出的解决方案)。

1
你可以将初始化逻辑从超级构造函数移动到受保护的init()方法,并在nodeId初始化后调用它。
也许可以使用更清晰的设计,但没有完整的示例很难提出建议。

不幸的是,我不能更改super()构造函数的行为,因为它来自第三方库。 - esin88
2
最好使用适配器模式,将第三方类移动到您的类实现中,而不是从它继承。 - Andrey Koshelev

0

你应该更改RemoteWebDriver后面的bean,将这个布尔比较添加到quit中。或者更好的方法是,在构造函数中不要调用任何方法,只构造实例本身并设置值。之后,评估一个新的实例变量布尔值isItFullyConstructed的值,如果为false,则调用原始的try/catch逻辑,这是导致问题的原因。这可行吗?


不好意思,很遗憾它不是。 - esin88

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接