【问题标题】:How to set as final a Java Object after the initialization初始化后如何将Java对象设置为最终对象
【发布时间】:2014-06-25 23:01:44
【问题描述】:

我有一个对象:

Object obj = create();

...我需要设置这个对象的一些属性:

obj.setParam("7696969", 0x506);

在这个过程之后,我需要确保obj 不能被修改。

有没有办法将obj 设置为final 而无需创建另一个复制objfinal Object

【问题讨论】:

  • 您正在寻找不可变类。
  • 在你的类中保留一个布尔值,指示你的数据是否已经初始化。在设置参数之前检查它,你就完成了。
  • 一个类声明为final并不意味着不能修改,只是不要扩展。
  • 而声明为final的字段只是表示该字段在初始化后不能修改,并不是说任何对象的字段地址都不能修改。

标签: java


【解决方案1】:

考虑为新创建的对象使用Builder 设计模式。

FooBuilder builder = new FooBuilder();
builder.setParam(...);
builder.setBar(...);

final Foo myFoo = builder.build();

请注意,即使myFoo 是最终的,它的成员字段也可能不是。你也不应该忘记声明那些 final。

【讨论】:

  • 注意这里的类型 Foo 如果不应该被修改的话,它需要是不可变的。
  • +1,顺便说一句,我认为对构建器使用调用链更优雅,例如: Foo myFoo = Foo.builder().withParam(...).withBar(. ..).build();
  • 您可能想看看Lombok's Builder annotation。它仍被标记为“实验性”,但它非常接近核心。
【解决方案2】:

final 关键字不会阻止对象的内部变量被修改。 final 关键字所做的就是防止变量被重新分配。

防止内部被修改需要对象本身的特定设计。

内部变量不能重新赋值的对象称为不可变对象。您可以使用私有访问修饰符创建不可变对象,并且不创建 set 函数。还要确保将所有内部变量声明为final

Amir 对 Builder 设计的引用是一个很好的建议。具有构建器模式的不可变对象非常适合您。

【讨论】:

  • 例如,Java String 和 Integer 类(以及许多其他类)是不可变的。
  • (如果您信任编写不可变类的人,则不必创建内部变量final。)
  • 非常真实的@HotLicks,但我不相信自己不会忘记一个困倦的早晨。我认为 final 关键字是对自己的保护:)
  • 我发现我在睡梦中做了一些我最好的编程。
【解决方案3】:

如果你真的需要通过 setter 单独设置参数,那么你将不得不做一些简单的算法。例如,当对象必须变为不可变时,您可以提供一个布尔标记,在初始化过程之后将该布尔值设置为 true,您的所有设置器将在决定是否可以使用新值重置当前值之前检查该布尔值。

当您没有本地机制为您完成这项艰巨的工作时,您必须想出一个可以完成这项工作的算法:)

【讨论】:

    【解决方案4】:

    你想要一个不可变的类。这是一个例子。

    class Foo
    {
        private string s;
        private int i;
    
        public Foo(string s, int i)
        {
            this.s = s;
            this.i = i;
        }
    
        public string getS() { return s; }
        public int getI() { return i; }
    }
    

    Builder 设计模式通常是矫枉过正。

    【讨论】:

    • 这仅在构造函数是设置值的唯一方法时才有效。如果在调用构造函数时您不知道所有值,而是一路添加它们怎么办?
    • 例如,当您想要双向引用时。
    • 那么需要更复杂的东西。这将取决于场景。我只是不想让 OP 留下这样的印象,即如果需要的只是不可变的,则需要 Builder 设计模式。
    【解决方案5】:

    不使用建造者模式,你可以做的是

        public class MyImmutableClass {
             final private String foo;
             final private String bar;
             public MyImmutableClass(String f, String b) {
                  this.foo = f;
                  this.bar = b;
             }
    
             // only getters
        }
    

    它的工作方式是

    1. 将所有字段设为最终字段
    2. 不提供二传手
    3. 确保子类不能覆盖任何方法(使类最终)

    如果构造函数中的参数数量增加,构建器模式会派上用场。您只是使用构建器使您的代码更易于维护和可读,但策略几乎相同,即它也只允许通过内部静态类进行对象操作并在主/外部类中公开只读(getter)方法

    使用 builder 的另一个原因可能是您的对象具有一些强制性和可选参数。

        public class Item {
           // private fields & getters
    
             public static class Builder {
                  // same private fields
                  public Builder(String f, String b) {
                     // set values
                  }
                  ...
                  ...
             }
         }
    

    在这里您可以使用内部public Builder(String f, String b) 构造函数来获取两个强制参数,将其余参数保持为可选

    如果参数数量较少,我认为第一种策略比实现构建器效果更好,因为它具有代码重复的缺点(构建器需要从外部类中复制所有字段)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多