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

时间:2010-07-13 19:10:20

标签: java oop immutability final

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

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

编辑:将“final”更改为“immutable”,因为这真的是我在语义上的意思。 (对不起:()

8 个答案:

答案 0 :(得分:14)

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

答案 1 :(得分:9)

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

答案 2 :(得分:7)

更好的方法可能是使用构建器,如第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...
}

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

Foo foo = Foo.builder()
    .setId(id)
    .build();

当然,您也可以将其拆分:

// 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();

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

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

答案 3 :(得分:6)

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

答案 4 :(得分:1)

你绝对不能成功final。如果setID()是个好主意,则从ID != null引发例外。也许如果你提供更多细节,有人可以提出更有创意的解决方案吗?

答案 5 :(得分:0)

除非在构造期间初始化该字段,否则无法声明该字段final。我认为这样的事情可能符合你的要求。

class Foo {

  private Bar x;

  void setX(Bar x) {
    if (x == null)
      throw new IllegalArgumentException();
    if (this.x != null)
      throw new IllegalStateException();
    this.x = x;
  }

}

Foo实现旨在供单个线程访问。对于多线程访问,您可以使用AtomicReference或外部synchronize访问权限。

另请注意,如果nullx的有效值,则可以创建一个私有的虚拟Bar实例,并使用它代替null

答案 6 :(得分:0)

也许这个好的lib会帮助你解析xml XStream,所以你不必为创建对象而烦恼。我不知道你是否可以按你想要的方式配置它,但我确定它值得一看。

我会尝试使用公共setId方法,因为如果没有某个客户端可以调用的问题,你可以解决这个问题。尝试使用该语言告诉用户他可以做什么以及他不应该做什么。

如果你没有将id存储在xml文件中,你应该使用一个工厂来创建对象并使用参数化的构造函数。

如果您没有为id提供setter,用户只能在创建对象时“设置”id,我认为这就是你想要的。

答案 7 :(得分:0)

从技术上讲,如果您可以在执行构造函数后更改该值,则它不是“不可变的”。它更“可模仿”。

但是除了术语之外:我想到的第一个想法就是拥有某种“验证”功能。浏览所有的安装人员,当您认为已完成时,请致电验证。如果失败,则该对象缺少必填字段或其他内容。

构建器对象的想法也很好。我从来没有使用过这种模式。 '必须考虑利弊。