Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何为最终变量使用setter而不是构造函数?_Java_Oop_Immutability_Final - Fatal编程技术网

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

Java 如何为最终变量使用setter而不是构造函数?,java,oop,immutability,final,Java,Oop,Immutability,Final,我正在解析一个XML文件,其中我希望不可变的字段ID必须在对象创建后设置。我应该将其设置为null,并在setID()方法中抛出一个异常(如果ID=空的 编辑: 我正在解析一个XML文件,在开始时,我创建了一个对象,其字段和对象是使用XML文件中的信息填充的。我希望能够在创建根对象后设置ID,该ID应该是不可变的 编辑:将“final”改为“immutable”,因为这就是我在语义上的真正意思。(抱歉:()您不能在构造函数之外更改最终成员。您必须使其不是最终成员。您绝对不能使其成为最终成员。从s

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

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


编辑:将“final”改为“immutable”,因为这就是我在语义上的真正意思。(抱歉:()

您不能在构造函数之外更改最终成员。您必须使其不是最终成员。

您绝对不能使其成为最终成员。
setID()引发异常)
如果
ID!=null
是个好主意。如果您提供更多细节,也许有人可以想出更具创造性的解决方案?

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

最常见的解决方法是。您使用setter构建一个对象,然后在该对象准备好后,使用e builder作为模板。

除非在构建过程中初始化,否则无法声明该字段为最终字段。我认为类似的内容可能满足您的要求

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
或外部
同步
访问


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

,也许这个很好的库将帮助您解析xml,因此您不必费心创建对象。我不知道您是否可以按您想要的方式配置它,但我当然值得一看

我会尽量避免使用公共的setId方法,因为如果没有客户端可以调用的方法,就可以解决这个问题。试着用这种语言告诉用户他可以做什么,不应该做什么

如果不在xml文件中存储id,则应使用创建对象并使用参数化构造函数的工厂


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

更好的方法可能是使用生成器,如第2项所述

基本思想是让一个生成器类为不同的构造函数参数提供setter(但通常不是getter)。还有一个
build()
方法。生成器类通常是用于构建的类的(静态)嵌套类。外部类的构造函数通常是私有的

最终结果如下所示:

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.builder()
    .setId(id)
    .build();
要创建Foo的实例,请编写如下内容:

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.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()或生成器的构造函数添加其他参数


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

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

撇开术语不谈:我想到的第一个想法是使用某种“验证”函数。检查所有设置程序,当您认为完成时,调用validate。如果失败,则表示对象缺少必填字段或其他内容


构建器对象的想法也很好。我从未使用过这种模式。我必须仔细考虑利弊。

我应该引发什么样的异常?我应该引发什么样的异常?@Aymon:这取决于你。如果这可能发生在系统中的多个类或字段中,请定义一个自定义公共类型,例如RepeatedAssignmentException@Aymon:我可能会使用IllegalStateException,或者从它派生的东西。我使用Google Guava checkArguments。它会抛出IllegalArgumentException,请参阅:我还有许多其他设置的字段,在我到达我想要存储在不可变字段中的信息之前它们不是不可变的。这是一个很好的解决方案awback可能是重复大量代码的繁琐过程,您可以将数据放入XML处理程序实例中。