如何允许在java中只设置一次变量

如何允许在java中只设置一次变量,java,final,setter,Java,Final,Setter,我需要在java中实现一个类,它的字段只需要设置一次,但在字段声明中它的值是未知的。为此,我必须: public class MyClass { private String field1 = null; public void setField1(String value) { if (field1 == null) { field1 = value; } } } 但是,我所做的并不能阻止用户多次调用setFi

我需要在java中实现一个类,它的字段只需要设置一次,但在字段声明中它的值是未知的。为此,我必须:

public class MyClass {
    private String field1 = null;
    public void setField1(String value) {
        if (field1 == null) {
            field1 = value;
        }
     }
}
但是,我所做的并不能阻止用户多次调用
setField1
。这可能会导致混淆,用户试图设置它(不知道它已在其他地方设置),然后用户会被
field1
没有给出他们设置的值而混淆-直到他们进入
setField1
方法查看发生了什么

我不喜欢的另一个解决方案是,让构造函数考虑
field1
的值,然后不提供setter。因为它使类更难扩展,当需要多个字符串字段时-在这种方法中,构造函数需要将一对
String
作为参数,然后用户很容易被参数混淆/丢失参数

显然,将
field1
设置为
final
也不起作用


我应该怎么做来防止我上面提到的问题?

这种情况下的常见做法是抛出
运行时异常。在您的情况下,最适合:

public void setField1(String value) {
    if (field1 != null) {
        throw new IllegalStateException("setField1 was already called");
    }
    field1 = Objects.requireNonNull(value);
}
还要注意,最好检查传递的字符串是否为null(我为此添加了)


您无法在编译时控制这一点,因为在不实际运行程序的情况下,无法分析程序以准确说明方法是否调用了一次或多次。

对于以下情况,您通常应该使用生成器模式:


生成器对象允许您设置所有喜欢的字段和选项,但它生成的MyClass实例不允许设置这些字段和选项。

如果不为null,则引发异常。然后调用方就会知道他们做错了什么;您可以在同一个实例上多次成功调用setField1。@Davidermann说得很好,谢谢您的提醒。虽然这样做很有效,但构建器模式更好—我喜欢不必抛出运行时异常的想法。生成器模式防止编码器走错方向。谢谢你!
MyClass instance = (new MyClassBuilder())
    .setField1(...)
    .setField2(...)
    .build();