如何使用setter而不是构造函数作为最终变量?

Aym*_*ier 3 java oop final immutability

我正在解析一个XML文件,其中我想要成为不可变的字段之一,ID,必须在创建对象后设置.如果ID!= null,我应该将它设置为null,并在setID()方法中抛出异常吗?

编辑:我正在解析XML文件,并在开始时创建一个对象,其字段和对象使用XML文件中的信息填充.我希望能够在创建根对象后设置ID,它应该是不可变的.

编辑:将"最终"更改为"不可变",因为这真的是我在语义上的意思.(抱歉:()

Mar*_*ers 14

最常见的方法是使用构建器模式.使用setter构建对象,然后在准备就绪后,使用构建器作为模板创建不可变对象.


Mar*_*ers 9

您无法在构造函数之外更改最终成员.你必须让它不是最终的.


Lau*_*ves 7

更好的方法可能是使用Builder,如第2项中的Effective Java 2nd Edition中所述.

基本思想是让Builder类具有不同构造函数参数的setter(但通常不是getter).还有一种build()方法.Builder类通常是用于构建的类的(静态)嵌套类.外部类的构造函数通常是私有的.

最终结果如下:

public class Foo {
  public static class Builder {
    public Foo build() {
      return new Foo(this);
    }

    public Builder setId(int id) {
      this.id = id;
      return this;
    }

    // you can set defaults for these here
    private int id;
  }

  public static Builder builder() {
      return new Builder();
  }

  private Foo(Builder builder) {
    id = builder.id;
  }

  private final int id;

  // The rest of Foo goes here...
}
Run Code Online (Sandbox Code Playgroud)

要创建Foo的实例,您可以编写如下内容:

Foo foo = Foo.builder()
    .setId(id)
    .build();
Run Code Online (Sandbox Code Playgroud)

当然,您也可以拆分它:

// I don't know the ID yet, but I want a place to store it.
Foo.Builder fooBuilder = Foo.builder();
...
// Now I know the ID:.
fooBuilder.setId(id);
...
// Now I'm done and want an immutable Foo.
Foo foo = fooBuilder.build();
Run Code Online (Sandbox Code Playgroud)

您可以在构建器上安装多个setter,甚至可以为build()或Builder的构造函数添加其他参数.

这允许您在解析时拥有可变对象,但在完成时切换到不可变对象.在许多情况下,您的API只需要公开不可变对象.


Uri*_*Uri 6

根据定义,最终字段在构造对象后不会改变.如果您真正想要的是您希望将字段设置一次,那么您可以简单地将该字段初始化为null,如果该字段不再为null,则该字段的setter会抛出异常.