Java中的不可变类

时间:2015-03-11 10:52:25

标签: java class immutability final

根据许多文档,我已经看到不可变类应具有以下功能:

  1. 课程应该是最终的

  2. 所有方法都应该是最终的

  3. 所有变量都应该是最终的

  4. 不应该有任何设定者

  5. 但我的问题是:

    1. 如果我的课程只包含最终变量怎么办?

    2. 如果我也有setter,我不能改变Object的状态,因为我拥有所有的最终变量。那么这将如何影响不变性呢?

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

3 个答案:

答案 0 :(得分:4)

  

1.如果我的班级只有final个变量?

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

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

    // ...
}

这允许某人做

myImmutableObject.arr[0] = "world";

并有效地改变不可变类的对象。

另外,建议禁止扩展类(因为没有办法强制子类是不可变的)。请参阅下面第三个问题的答案。

  
      
  1. 如果我也有setter,我不能改变Object的状态,因为我有所有的最终变量。那么这将如何影响不变性。
  2.   

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

  
      
  1. 在这种情况下,继承如何更改对象状态?
  2.   

子类不能更改超类的最终字段的状态。但继承存在另一个问题。

如果Animal有一个不可变的Dog子类,并且Dog有一个setDogsName方法可以改变对象,那么实际上你可能有Animal 1}}对象(类型Dog)实际上是可变的。

换句话说,如果不可变类是为扩展打开的话,大多数(全部?)不可变性的好处都会丢失:例如,如果你收到Animal作为方法的参数,你不能认为它是不可变的。您无法安全地将Animal个对象作为哈希映射等中的键。


基本上原始陈述有点多余,这就是为什么我认为它们有点令人困惑:

  • 无法扩展final类,因此将方法标记为final
  • 也是多余的
  • 如果所有变量都是最终的,那么说不应该有任何设置者是多余的。

此外,这些是足够的约束,但不是必需的。例如,你可以拥有不具有最终变量/最终字段类型的不可变类,只要它们是私有的,永远不会在内部更改并且永远不会泄露给外人。

答案 1 :(得分:2)

  

如果我也有setter,我无法更改Object的状态,As   我有所有的最终变量。那么这将如何影响不可动摇性呢。

final位于引用级别,不变性位于实例级别。

class someMutableClass{
final List someList;
}

在上面的代码中。如果列表的引用转义,那么任何人都可以: someList.add(someValue)

但他们做不到:

someList=someOtherList;

这就是区别。

  

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

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

答案 2 :(得分:2)

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


但首先,让我们来看看"应该有"列表:

  • "课程应该是最终的" - 这可能是可取的,但可能不是绝对必要的......甚至是可取的。即使某些子类的实例是可变的,类的实例 1 也可以是不可变的。这完全取决于哪些类需要是不可变的,这取决于上下文。

  • "所有方法都应该是最终的" - 既不必要也不充分。如果该类为final,那么这是不必要的。如果课程不是final,那么这还不够。 (您可以在子类中添加具有不同签名的方法。)

  • "所有变量都应该是最终的" - 既不必要也不充分。您可以拥有一个字段不是final的不可变类,以及一个字段都是final的可变类。

  • "不应该有任何制定者" - 这取决于你所说的" setter"的意思,但这又不是必要的(对于某些可变性模型)或足够的。


您的问题:

  

1)如果我的班级只有最终变量怎么办?

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

  

2)如果我也有setter,我不能改变Object的状态,因为我有所有的最终变量。那么这将如何影响不可动摇性呢。

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

  

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

它不能 1 。但那不是重点。

创建不可变类final的原因是阻止某人创建该类的可变子类

为什么重要?

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

这是(例如)String类为final的主要原因。


1 - 我需要对此进行限定。这取决于我们是在谈论类为A的实例,还是与A类型兼容的实例。我在谈论前者。存在可变子类的事实确实会影响其类为A的实例的可变性。