【问题标题】:Strategy for Immutable Classes - Is this a contradiction?不可变类的策略 - 这是矛盾的吗?
【发布时间】:2017-08-06 13:00:21
【问题描述】:

strategy for defining an immutable class 包含 2 个点:

  1. 将该字段标记为私有和最终字段
  2. 不提供二传手

我的困惑是:当我将该字段标记为 private final 时,省略 setter 会带来什么额外的安全性?

由于该字段是私有的,没有类方法就不能在类外访问。但是因为也是final的,所以初始化后就不能修改了。

假设,在下面的类中,如果我没有在突出显示的 2 个地方初始化我的字段,那么编译器会给我一个错误,即最终的空白字段尚未初始化,这意味着 将文件标记为最终的,不会' t 让我在不初始化 final 字段的情况下构造一个对象,这意味着在对象创建后只会存在这样一个字段的 1 个值

class MyImmutable {
  private final int field1; // either initialze here

  MyImmutable() {
     this.field1 = ... ; // or here
  }
}

【问题讨论】:

  • 你没有意识到链接的文章是一个食谱,其中第一步(不是第二步!)是“不提供“setter”方法”,然后第二步(不是第一步!)是“制作所有字段finalprivate。按照这个顺序,它比您在问题中提出的顺序更有意义。文章中列出的这四个步骤提供了一种不可变的感觉。
  • private final 不能确保不变性。 MyImmutable 类型可以由一个可变对象组成,该对象由 setter 委托给(在分解现有代码时很常见)。只要 Setter 不改变状态,它们就很好。您的示例仅是合理的,因为它仅由原始类型组成。如果 MyImmutable 由可变引用类型组成,那就另当别论了。
  • @VinceEmigh 链接文章中的其他两个步骤已解决。
  • @MarkRotteveel 是的,它已通过第四步解决。但是该声明清楚地说明了它对“二传手”的定义。以下语句,例如标记字段private final,将使第一个语句毫无意义。但这绝对是他们试图理解的重点:“这个类中有两个 setter 方法。......第二个,invert,可以通过创建一个新对象而不是修改现有对象来适应一个。" - 可以说他们用尽了相似的点,我个人会说他们对单个点(不变性)很明确。

标签: java immutability


【解决方案1】:

不。这篇文章只是明确说明了不可变类型的属性。

他们引用了“setter”这个词,并给出了精确的定义。确切的报价指出:

不提供“setter”方法——修改字段或字段引用的对象的方法

private final 并不总是确保不变性。在您的示例中,private final 就足够了,因为 field1 是原始类型。

仔细看看报价:

修改字段的方法字段引用的对象

如果MyImmutable 由可变类型组成,setter 可以将调用委托给它们,从而导致MyImmutable 发生变异,无论MyImmutable 中的字段是否标记为private final

这是一个使用 private final 的示例,但该类型包含一个可变类型,由 setter 委托调用:

@Immutable
class Person {
    private final Identity identity; // Identity is a mutable type

    public void changeNameTo(String name) {
        identity.changeNameTo(name); // private final can't prevent this
    }
}

@Mutable
class Identity {
    private String name;

    public void changeNameTo(String name) {
        this.name = name;
    }
}

这是decomposing现有代码时的常用策略。

【讨论】:

  • 我一直认为 setter 只会将一些外部值愚蠢地分配给本地字段值。只是出于好奇,我们是否也将changeNameTo 称为二传手?总有几种方法可以使你的类变得可变,但这些方法在这里没有上下文。
  • @RajeevRanjan 这正是它正在做的事情,特别是如果编译器被告知内联 Identity#changeNameTo (将导致类似于来自 Identity#changeNameTo 的代码被直接放回 @987654334 中的字节码@):所有行为都是调整属性的状态。这里唯一的区别是委托,它不会改变理念。
猜你喜欢
  • 2021-07-13
  • 2019-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-14
相关资源
最近更新 更多