繁体   English   中英

Java不可变类规则

[英]Java immutable-class rules

以下类是不可变的:

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

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

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

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

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

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

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

作为补充说明:

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

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

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

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);
    }
}

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

记住这可能是一种痛苦,但有一种工具可以帮助你。

Pure4J提供了一个注释@ImmutableValue ,您可以将其添加到接口或类中。

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

希望这可以帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM