Java 中的不可变 class

Immutable class in Java

根据许多文档,我看到不可变 class 应该具有以下特征:

  1. class 应该是最终的

  2. 所有方法都应该是final

  3. 所有变量都应该是final

  4. 不应该有任何设置器

但我的问题是:

  1. 如果我有一个只有 final 变量的 class 怎么办?

  2. 如果我也有 setter,我无法更改对象的状态,因为我有所有最终变量。那么这将如何影响不变性?

  3. 在这种情况下,继承如何改变对象状态?

1.What if I have a class with only final variables?

这会让你走得很远,但不会一路走来。这些变量的类型也需要是不可变的。例如考虑

class MyImmutableClass {
    // final variable, referring to a mutable type
    final String[] arr = { "hello" };

    // ...
}

这允许某人做

myImmutableObject.arr[0] = "world";

并有效地改变不可变 class.

的对象

此外,建议禁止扩展 class(因为无法强制子class 不可变)。请参阅下面第三个问题的答案。

  1. If I do have setters also, I cannot change the state of the Object, As i have all the final variables. so how will this affect immutability.

没错。如果所有变量都是最终变量,标准 setter 方法将不存在。

  1. how can inheritance change the object state in this case?

subclass 不能改变 superclass 的 final 字段的状态。但是继承还有另外一个问题。

如果你有一个不可变的 Animal subclass 由 Dog 编辑,并且 Dog 有一个 setDogsName 方法来改变对象,那么实际上你可能有 Animal 个对象(Dog 类型)实际上是可变的。

换句话说,如果不可变 class 开放扩展,那么不可变的大部分(全部?)好处都会丢失:例如,如果您收到 Animal 作为方法的参数,您不能假设它是不可变的。您不能安全地将 Animal 对象作为哈希映射中的键等


基本上原来的陈述有点多余,这就是为什么我觉得它们有点混乱:

  • A final class 无法扩展,因此将方法也标记为 final
  • 是多余的
  • 如果所有变量都是最终变量,那么说不应该有 setters 有点多余。

另外,这些是足够的约束,但不是必需的。例如,您可以拥有不带 final 变量/final 字段类型的不可变 classes,只要它们是私有的,从不在内部更改并且从不泄露给外部。

If i do have setters also,i cannot change the state of the Object, As i have all the final variables. so how will this affect immutablity.

final 处于 reference 级别,immutability 处于 instance 级别.

class someMutableClass{
final List someList;
}

在上面这段代码中。如果列表的引用逃脱了,那么任何人都可以这样做: someList.add(someValue)

但是他们做不到:

someList=someOtherList;

这就是区别。

how can inheritence change the object state in this case?

子 class 可以访问父 class 的某些字段,然后对其进行更改。您可以使父 class 引用指向子 class 对象并修改其字段。因此,为了确保不变性,您必须确保子 class 不会更改父项中的任何内容。所以让它成为 final.

不可变的 class 是您无法更改的。实现不变性是消除改变对象状态的可能方式的问题。这可以通过结合结构和行为手段来实现。


但首先,让我们看一下 "should have" 列表:

  • "class should be final" - 这可能是可取的,但它可能不是绝对必要的......甚至不是可取的。 class 的实例1 可以是不可变的,即使某些子class 的实例是可变的。这完全取决于哪些 classes 需要 是不可变的,这取决于上下文。

  • "all the methods should be final" - 既不必要也不充分。如果 class 是 final 那么就没有必要了。如果 class 不是 final,那么它是不够的。 (您可以在子class中添加具有不同签名的方法。)

  • "all the variables should be final" - 既不必要也不充分。你可以有一个不可变的class,它的字段不是final,还有一个可变的class,它的字段都是final.

  • "there should not be any setters" - 这取决于你所说的 "setter" 的意思,但是这又是不必要的(对于某些可变性模型)或足够的。


您的问题:

1) What if i have a class with only final variables ?

这不足以保证不变性。例如,如果最终变量之一是数组,则可以更改该数组的状态,从而更改整个对象的状态。这可以通过 setter(或 class 的任何其他方法)来完成,或者如果 class 是 "leaky abstraction" 那么它可以通过外部代码来完成。

2) If i do have setters also,i cannot change the state of the Object, As i have all the final variables. so how will this affect immutablity.

见上文。将所有字段声明为 final 并不能保证不变性。 setter 可以更改对象的可变 组件 的状态。

3) how can inheritence change the object state in this case?

不能1。但这不是重点。

制作不可变 class final 的原因是为了阻止某人创建 可变子 class class.

为什么这很重要?

好吧,假设一个方法要求 参数是不可变的(例如为了安全)。如果你用一个不可变的 class 而不是 final 来声明它,那么有人会创建一个可变的子 class 并传递它的一个实例而不是原始的不可变 class.

这就是为什么(例如)String class 是 final 的主要原因。


1 - 我需要证明这一点。这取决于我们谈论的是 class 是 A 的实例,还是与 A 类型兼容的实例。我说的是前者。事实上,有一个可变的 subclass 确实会影响 class 为 A.

的实例的可变性