Java不可变类规则

时间:2010-06-29 16:44:55

标签: java immutability

以下类是不可变的:

final class MyClass {
    private final int[] array;
    public MyClass(int[] array){
        this.array = array;
    }
}

5 个答案:

答案 0 :(得分:21)

不,不是因为数组的元素仍然可以更改。

int[] v1 = new int[10];
MyClass v2 = new MyClass(v1);
v1[0] = 42;  // mutation visible to MyClass1

答案 1 :(得分:6)

关于不变性规则的两分钱(我从阅读有效Java中保留了这一规定!):

  1. 不提供可以修改对象状态的方法。
  2. 让所有字段都成为最终字段。
  3. 确保您的课程不可扩展。
  4. 将您的所有字段设为私有。
  5. 提供对可以更改的班级的任何字段或组件的独占访问权限。基本上这适用于您的情况(如explained by JaredPar)。使用您的类的人仍然可以引用您的数组。相反的情况是,您将引用返回到类的组件。在这种情况下,请始终创建防御性副本。在您的情况下,您不应该分配参考。而是将您的类用户提供的数组复制到内部组件中。

答案 2 :(得分:1)

“不变性”是程序员和他自己之间的惯例。该规范可能或多或少地由编译器强制执行。

如果类的实例在应用程序代码执行的正常过程中没有更改,则它们是“不可变的”。在某些情况下,我们知道它们不会改变,因为代码实际上禁止它;在其他情况下,这只是我们如何使用该类的一部分。例如,java.util.Date实例是正式可变的(它上面有一个setTime()方法)但是习惯上将它处理为就像它是不可变的一样;这只是一个应用程序范围的约定,Date.setTime()方法不会被调用。

作为补充说明:

  • 通常根据“外部特征”来考虑不变性。例如,Java的String被记录为不可变的(这就是Javadoc所说的)。但是,如果查看源代码,您将看到String实例包含一个名为hash的私有字段,该字段可能会随时间而变化:这是hashCode()返回的值的缓存。我们仍然说String是不可变的,因为hash字段是内部优化,从外部看不到任何效果。
  • 通过反射,如果程序员非常愿意,可以修改最私密的实例字段(包括那些标记为final的字段)。并不是说这是一个好主意:它可能会破坏使用所述实例的其他代码段所使用的假设。正如我所说,不变性是一个惯例:如果程序员想要自己打,那么他可以,但这会对生产力产生不利的副作用......
  • 大多数Java值实际上是引用。由您来定义引用的对象是否是您认为是“实例内容”的一部分。在您的类中,您有一个引用(外部提供的)整数数组的字段。如果之后修改了该数组的内容,您是否会认为这会破坏MyClass实例的不变性?这个问题没有通用答案。

答案 3 :(得分:0)

无法使数组成为不可变的。也就是说,没有办法阻止任何客户端代码设置或删除或添加项目到数组。

这是一个真正不可改变的选择:

private static class MyClass
{
    private List<Integer> list;

    private MyClass(final int[] array)
    {
        final List<Integer> tmplist = new ArrayList<Integer>(array.length);
        for (int i : array)
        {
            tmplist.add(array[i]);
        }
        this.list = Collections.unmodifiableList(tmplist);
    }
}

答案 4 :(得分:0)

要使类不可变,您需要确保其上的所有字段都是最终的,并且这些字段的类型也是不可变的。

这可能是一件难以忘怀的事,但有一种工具可以帮助你。

Pure4J提供了注释@ImmutableValue,您可以将其添加到界面或类中。

有一个maven插件可以在编译时检查你是否符合此后的不变性规则。

希望这有帮助。