为什么字段在不可变类中应该是最终的?

时间:2013-09-04 07:32:47

标签: java immutability final

用于定义不可变类的

Strategy表示

所有字段都应该是最终字段。

例如:

private String name;

为什么必须是最终的?

因为我没有给它设置setter方法?它无法改变。 感谢。

6 个答案:

答案 0 :(得分:16)

如果您阅读

private final String name;

你知道该字段是不可变的。

如果您阅读

private String name;

你必须阅读整个班级以检查它是否在任何地方都没有改变。这对你来说意味着更多的工作。

你现在可能还记得,刚刚写过你没有添加二传手的课程,但是在写完了更多的课程后,你会在六个月后阅读自己的课程,你将无法可靠地记住。

即使现在没有改变,有人(可能是你自己)也可以通过添加代码来改变它。但是,您可能已经假设值不会改变。

简而言之,只有在你意味着要改变的价值时才将其作为非最终结果,并在你不期望它改变时将其作为最终结果。不要把它留在可能/可能不是。


现在想象一下,您已经习惯了解哪些字段可以更改,哪些字段无法更改。在阅读其他代码时,这可以为您节省大量工作。但是你发现你正在阅读不清楚的代码并且非最终并不意味着它已经改变了,现在意味着你必须检查一下,你通常不必检查哪一个更难以理解一些你真的不需要的代码。


一个简单的例子,说明读取代码以确定某个字段是否有效最终是多么困难。

public class A {
    static class B  {
        private int x;
    }

    // some code

到目前为止,这一切看起来都很好,B中没有制定者甚至方法。那么B.x是不可改变的吗?

    static class C {
        public void update(B b, int x) {
            b.x = x; // this really compiles
        }
    }
}

哎呀,你必须阅读整个班级文件。

当你编写代码时,你可以更好地创建每个字段final(应该是默认的恕我直言),而不是留给别人稍后弄清楚。

答案 1 :(得分:3)

  • 保持字段final强调它无法在其他任何地方进行更改。
  • 不应更改字段的自我记录代码
  • 如果您在其他地方更改字段,编译器会通过提供错误来帮助您

所以final在许多方面帮助使对象成为不可变的。

答案 2 :(得分:1)

主要原因(恕我直言)是当field为final时,保证在构造函数完成后立即在其他线程中可见。

答案 3 :(得分:1)

最好将不可变字段设为final,即使在其他可变对象上也是如此。

请注意,一个对象的私有字段实际上可以由同一类的其他实例访问。

如果对象(类或实例)的内部状态无法更改(反射不计算),则该对象(类或实例)是不可变的。 最后一个字段保证只能保证值(如果是基元)或引用(对于非基元)不能更改。
对于非基元,这并不自动意味着引用的值也是不可变的。这意味着如果您的最终字段引用(例如,列表),则无法交换列表,而是添加/删除其中的值,从而更改对象的状态。

对于一个不可变的对象:

  • 内部状态必须在构建时确定,永远无法更改
  • 这意味着定义状态的所有字段必须是最终的(您可能有其他不属于该状态的帮助字段,这很好但很少见。)
  • 这也意味着所有引用的对象必须是不可变的。一些对象(如String)已经是不可变的,其他对象(如集合)可以被包装以使它们不可变(Collections.immutableList|Set|Collection|...

答案 4 :(得分:0)

使原始类型最终确保不变性。然而,使非原始对象最终有时没有意义,因为最终的对象状态可以被改变。正如Greg所指出的,这取决于所讨论的对象的类型

如您所示的示例,所有属性都是原始的,因此最终的keword是有意义的。

答案 5 :(得分:0)

声明字段final的一个好处是它允许编译器检测在重构期间更改字段的尝试。即使一个类的字段不是最终的,它也可以是不可变的。