Java不可变类与静态工厂而不是构造函数



当创建一个可变类而不使用final时,如

public final class X {...}

而是只使用常规的类声明而没有 final,如

public class X {...}

然后使用静态工厂构造函数,使用私有构造函数,其中所有字段都是私有的,那么我的问题是 -仅声明私有字段而不声明最终字段还不够吗,例如

private double d;

或者/为什么我还要说

private final double d;

我唯一的答案是,我不会错误地在班级内改变任何非最终字段,但是任何人都可以从外部更改吗?

不可变的类意味着在创建该类的实例后无法更改类状态和/或。使类不可变可以通过多种方式完成;使用final变量会有所帮助,并且使类本身final可以通过防止子类可能破坏不可变性规则的子类来提供帮助。

在类上使用final关键字与在变量上使用它意味着两件完全不同的事情。

将类设置为 final 意味着该类不能被子类化;也就是说,如果你有

public final class X {...}

您无法扩展它

public class Y extends X {...} // Illegal!

类是否为最终与类成员变量是否为最终无关。

将变量设置为final,无论是static、类成员变量还是局部(方法或块)变量,都意味着变量在被赋值后无法更改。

您的代码还需要通过任何可用的执行路径为最终变量设置一个值,所以这没关系,因为它总是将"max"设置为某些内容:

...
final int max;
if (parameter) {
max = 100;
} else {
max = 1000;
}

但这会给你一个错误:

...
final int max;
if (parameter) {
max = 100;
}

因为如果"参数"的计算结果为true,它会设置"max",否则永远不会设置"max"。
这通常在不可变对象的构造函数中完成:

public final class X
{
final int value;
public X(int v)
{
this.value = v;   // Now that `value` has been set
// it can never be changed
}
}

将一个类声明为final并不意味着它的类成员是最终的,所以你的问题"为什么我还要说">是因为final的这两种用法是不相关的。

请注意,您还可以将方法声明为 final:

public final int getMax() {...}

这意味着不允许子类使用新实现重写此方法。(如果类已经final,则方法上的final没有意义,因为不可能有任何子类尝试重写该方法。

无论您是否添加final,没有人能够触及该类代码之外的这些值。

如果可以的话,最好将它们声明为最终的,因为它确实为编译器提供了有关它们行为方式的进一步线索,以便它可以更积极地优化代码。最后一个字段也是线程安全的,而您可能会在延迟初始化中弄乱非私有字段的状态。

我的建议是,你总是尽可能多地声明最终的,除非有明显的性能提升,否则不要做延迟/延迟初始化。

同样重要的是要记住,对数组或不可变对象的final引用并不能保证如果您共享引用值(例如,直接通过 getter 返回它),其内容不会更改。裸数组没有解决方法,但对于java.util.*集合,您始终可以共享对集合的不可修改包装引用(请参阅Collections.unmodifiableXXXX方法)。

最后请注意,尽管将字段声明为带或不带finalprivate,但仍然可以使用一些时髦的低级检测来更改这些值(例如,请参阅此值)。但是,在编程时,没有人应该真正担心这一点。

相关内容

最新更新