如何对 Final 变量使用 setter 而不是构造函数?

发布于 2024-09-09 16:14:31 字数 234 浏览 6 评论 0原文

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

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

编辑:将“最终”更改为“不可变”,因为这确实是我的语义含义。 (对不起 :( )

I am parsing an XML file where one of the fields I want to be immutable, ID, has to be set after the object is created. Should I set it to null, and throw an exception in the setID() method if ID!=null ?

Edit:
I am parsing an XML file, and at the beginning, I create an object whose fields and objects are populated using information in XML file. I want to be able to set ID, which should be immutable, after creating the root object.

Edit: changed "final" to "immutable" because that's really what I meant semantically. (Sorry :( )

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

じее 2024-09-16 16:14:32

解决此问题的最常见方法是使用构建器模式。您使用 setter 构建一个对象,然后一旦准备好,您就可以使用构建器作为模板创建不可变对象。

The most common way around this is to use the builder pattern. You build up an object using setters and then once it's ready you create the immutable object using the builder as a template.

江湖正好 2024-09-16 16:14:32

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

You cannot change a final member outside of a constructor. You will have to make it not final.

怀中猫帐中妖 2024-09-16 16:14:32

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

基本思想是拥有一个 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();

您可以在构建器上有多个设置器,甚至可以向 build() 或构建器添加其他参数构造函数。

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

A better approach might be to use a Builder, as described in Effective Java 2nd Edition in Item 2.

The basic idea is to have a Builder class that has setters (but usually not getters) for the different constructor parameters. There's also a build() method. The Builder class is often a (static) nested class of the class that it's used to build. The outer class's constructor is often private.

The end result looks something like:

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...
}

To create an instance of Foo you then write something like:

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

You can also split this up, of course:

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

You can have multiple setters on the builder, and even add additional parameters to build() or the Builder's constructor.

This lets you have a mutable object while parsing, but switch to an immutable object when you're finished. Your API only ever need expose the immutable object in many cases.

混吃等死 2024-09-16 16:14:32

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

Final fields, by definition, do not change after construction of the object.
If what you really mean is that you want the field to be set once, then you could simply initialize the field to null have the setter for that field throw an exception if the field is no longer null.

我家小可爱 2024-09-16 16:14:32

你绝对无法将其定为最终。如果 ID != null 则从 setID() 抛出异常是个好主意。也许如果您提供更多细节,有人可以想出更有创意的解决方案?

You definitely cannot make it final. Throwing an exception from setID() if ID != null is a good idea. Maybe if you provide some more details, someone can come up with a more creative solution?

酷遇一生 2024-09-16 16:14:32

该字段不能被声明为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 实例并使用它代替 >空。

The field cannot be declared final unless it is initialized during construction. I think something like this might meet your requirements.

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;
  }

}

This Foo implementation is intended for access by a single thread. For multi-thread access, you could use an AtomicReference or externally synchronize access.

Also note that if null is a valid value for x, you can create a private, dummy Bar instance and use it in place of null.

单挑你×的.吻 2024-09-16 16:14:32

也许这个不错的库将帮助您解析 xml XStream,这样您就不必担心对象创建。我不知道你是否可以按照你想要的方式配置它,但我确信它值得一看。

我会尝试避免使用公共 setId 方法,因为如果您没有某个客户端可以调用的方法,您就可以摆脱这个问题。尝试用语言告诉用户他可以做什么和不应该做什么。

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

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

Maybe this nice lib will help you whith parsing xml XStream, so you don't have to bother about object creation. I don't know if you can configure it the way you want, but im sure its worth a look.

I would try to aviod a public setId method, since you get rid of this problem, if you don't have one which some client could call. Try to use the language to tell the user what he can do and what he should not.

If you don't store the id in the xml file you should use a factory which creates the object and uses a parameterized constructor.

If you don't provide a setter for the id, users can only "set" a id when creating the object, and I think thats what you want.

抹茶夏天i‖ 2024-09-16 16:14:32

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

但抛开术语不谈:我想到的第一个想法是拥有某种“验证”功能。检查所有设置器,当您认为完成时,调用验证。如果失败,则该对象缺少必填字段或其他内容。

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

Technically if you can change the value after executing the constructor, it is not "immutable". It's more "semimutable".

But terminology aside: The first idea that occurs to me is to have a "validate" function of some sort. Go through all your setters, and when you think you're done, call validate. If it fails, the object is missing required fields or whatever.

The idea of a builder object is good too. I've never used that pattern. 'Have to think through the pros and cons.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文