java - 创建不可变类的最佳设计方法

标签 java immutability final

我正在阅读 Effective Java 中创建不可变类时需要遵循的具体指南.

我读到在不可变类中方法不应被覆盖,否则被覆盖的方法可能会改变方法的行为。以下是 java 中可用于解决此问题的设计方法:-

  1. 我们可以将类标记为 final,但根据我的理解,它有一个缺点,即它使类不可扩展。

  2. 其次是使个别方法成为 final方法,但除了我们需要单独将每个方法标记为 final方法以防止覆盖之外,我没有其他缺点。

  3. 根据书本,更好的方法是将构造函数设为私有(private)或包私有(private),并提供用于创建对象的公共(public)静态工厂方法。

我的问题是:即使我们在类中包含私有(private)或默认构造函数,它也不能再在同一个包中扩展(在包私有(private)构造函数的情况下在其他包中),它有与第一个相同的问题.它如何被认为是比以前的方法更好的方法?

最佳答案

不可变对象(immutable对象)不应该是可扩展的。为什么?

因为扩展它将允许直接访问字段(如果它们是 protected 将允许编写更改它们的方法),或者添加可能可变的状态。

假设我们编写了一个类 FlexiblyRoundableDouble 来扩展 Double,它有一个额外的字段 roundingMode 让我们选择一个“舍入模式”。您可以为此字段编写一个 setter,现在您的对象是可变的。

你可以争辩说,如果所有的方法都设置为final,你就不能改变对象的原始行为。唯一可以访问您的 roundingMode 字段的方法是新方法,如果您将对象分配给 Double 变量,则这些方法不能多态使用。但是当一个类的契约(Contract)说它是不可变的时,你就可以根据它做出决定。例如,如果您为具有 Double 字段的类编写 clone() 方法或复制构造函数,您知道您不需要深度复制 Double 字段,因为它们不会改变它们的状态,因此可以在两个克隆之间安全地共享。

此外,您可以编写返回内部对象的方法,而不必担心调用者随后会更改该对象。如果该对象是可变的,则您必须制作它的“防御性副本”。但如果它是不可变的,则返回对实际内部对象的引用是安全的。

但是,如果有人将 FlexiblyRoundableDouble 分配给您的其中一个 Double 字段,会发生什么情况?该对象将是可变的。 clone() 会假设它不是,它将在两个对象之间共享,甚至可能由方法返回。然后,调用者可以将其转换为 FlexiblyRoundableDouble,更改字段...这将影响使用同一实例的其他对象。

因此,不可变对象(immutable对象)应该是最终的。


这一切都与构造函数问题无关。对象可以通过公共(public)构造函数安全地保持不变(如 StringDoubleInteger 和其他标准 Java 不可变对象(immutable对象)所证明的那样)。静态工厂方法只是利用对象不可变这一事实的一种方式,并且其他几个对象可以安全地持有对它的引用,从而创建更少的具有相同值的对象。

关于java - 创建不可变类的最佳设计方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29470658/

相关文章:

java - spring-boot 创建 logging.path_IS_UNDEFINED.log 文件

Java - JButton 与变化的背景

java - 在 Linux 中运行的免费实用程序,用于从 Java 源文件创建 UML 类图

reactjs - 如何使用react hook useState添加到对象?

java - 什么是好的嵌入式java应用服务器

python - 克服字符串不可变性的 "disadvantages"

python - Python 中模块级常量的元组与列表?

Swift - private var/func 与 private final var/func

java - 为什么不可变类需要 final 关键字?

java - 泛型和关键字final