所有字段都应该是最终的。
例如:
private String name;
为什么它必须是最终的?
既然我没有为它提供setter方法?它不能改变。谢谢。
如果你读
private final String name;
你知道这个领域是不可变的。
如果你读
private String name;
您必须阅读整个课程以检查它在任何地方都没有更改。这对您来说意味着更多的工作。
您现在可能记得,刚刚编写了您没有添加 setter 的类,但是在编写了更多类之后,您在六个月后阅读了自己的类,您将无法可靠地记住。
即使现在没有更改,以后有人(可能是您自己)也可以通过添加代码来更改它。但是,您可能已经假设该值不会改变。
简而言之,只有当你的意思是改变的值时才让它成为非最终的,当你不期望它改变时让它成为最终的。不要把它当作可能/可能不是。
现在想象一下,您已经习惯于清楚哪些字段可以更改,哪些字段不能更改。这可以在阅读其他代码时为您节省大量工作。但是您发现您正在阅读的代码不清楚且非最终的代码并不意味着它已被更改,这意味着您现在必须检查事物,通常您不必检查试图理解哪个更令人头疼一些你真的不需要的代码。
一个简单的例子,说明阅读代码以确定一个字段是否有效地最终是多么困难。
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
(这应该是默认的恕我直言),而不是将其留给以后的人弄清楚。
主要原因(恕我直言)是当字段为 final 时,保证在构造函数完成后立即在其他线程中可见。
final
强调了它不能在其他任何地方更改的事实。因此final
在许多方面都有助于使对象不可变。
将不可变字段设为最终字段是一种很好的做法,即使在其他可变对象上也是如此。
请注意,一个对象的私有字段实际上可以被同一类的其他实例访问。
一个对象(类或实例)是不可变的,如果它的内部状态不能改变(反射不算)。使字段最终只能保证值(如果它是原始的)或引用(对于非原始的)不能更改。
对于非原语,这并不意味着引用的值也是不可变的。这意味着如果您的最终字段引用,例如,一个列表,则不能交换该列表,而是从中添加/删除值,从而改变对象的状态。
对于一个不可变的对象:
Collections.immutableList|Set|Collection|...
)使原始类型最终确保不变性。然而,使非原始对象成为最终对象有时是没有意义的,因为最终对象状态可能会发生变异。正如 Greg 指出的,这取决于所讨论对象的类型
正如您展示的示例,所有属性都是原始的,因此最终的关键字是有意义的。
声明字段 final 的一个好处是它允许编译器检测在重构期间更改字段的尝试。一个类可以是不可变的,即使它的字段不是最终的。