10

已经阅读了如何通过以下步骤使类不可变

  1. 不要提供“setter”方法——修改字段或字段引用的对象的方法。
  2. 将所有字段设为最终字段和私有字段。
  3. 不允许子类覆盖方法。最简单的方法是将类声明为 final。更复杂的方法是使构造函数私有并在工厂方法中构造实例。
  4. 如果实例字段包含对可变对象的引用,则不允许更改这些对象
    :不要提供修改可变对象的方法。
    湾。不要共享对可变对象的引用。永远不要存储对传递给构造函数的外部可变对象的引用;如有必要,创建副本并存储对副本的引用。同样,在必要时创建内部可变对象的副本以避免在方法中返回原始对象。

我不确定我是否清楚地了解私有构造函数和工厂方法在不变性上下文中的效用。如果我将课程定为最终课程,则基本上是关闭任何其他扩展它的课程的所有路径。所说的更复杂的方法如何

我已经看到了单例模式中的私有构造函数、工厂方法,这是有道理的。但是当我们谈论对象不变性时,当我们提到私有构造函数和静态工厂方法时,我们是否也在限制对象构造/实例化?

4

8 回答 8

3

首先,不可变类通常不应该被覆盖有几个原因,你可以在这里找到它们。

也就是说,将构造函数设为私有只是防止类被覆盖的一种方法。为什么?因为在子类中,每个构造函数(隐式)调用super(),基类的默认构造函数。但是,如果您将此构造函数设为私有,子类将无法调用它,因此无法覆盖基类。当您想要控制特定类的实例总数时,这种方法非常适合,例如在单例的情况下。

于 2012-11-30T17:14:14.457 回答
3

仔细阅读以下几点后

不允许子类覆盖方法。最简单的方法是声明类 final。更复杂的方法是使构造函数私有并在工厂方法中构造实例。

我认为这里的重点不是

使类最终和私有构造函数。

重点是

你要么让你的类成为最终的,要么有一个私有的构造函数。

希望能帮助到你!!

资源

于 2018-05-27T12:10:38.283 回答
2

我认为这里的大问题是未来的重构。假设在以后的版本中,您发现如果您可以将 MyClass 的一些新特例拆分为子类 MySpecialClass,这会使事情变得更简单。

如果 MyClass 是具有公共构造函数的可变类,您可以这样做并告诉新功能的用户创建一个新的 MySpecialClass。现有用途不受影响。

如果 MyClass 有私有构造函数和工厂方法,就没有问题。您声明 MySpecialClass 嵌套在 MyClass 中,也使用私有构造函数。添加和/或修改工厂方法以选择要创建的方法,但要确保现有调用继续兼容地工作。

如果 MyClass 是不可变的、最终的,但有一个公共构造函数,你会怎么做?

于 2012-11-30T17:46:20.193 回答
1

嗯你是对的。将构造函数设为私有没有任何意义。通过这样做,我们限制了实例的创建,这不是不变性的理想场景。

在 sun 站点中提到的示例中,没有将构造函数设为私有 http://docs.oracle.com/javase/tutorial/essential/concurrency/syncrgb.html

于 2012-11-30T17:08:44.793 回答
1

不变性- 对并发非常有帮助,因为它避免了在线程环境中可能创建的各种不变量。

FACTORY METHODS - 只是一个命名约定,因为它们更冗长的阅读和易于理解的自定义名称。例如:copyOf()方法比创建复制构造函数更有意义。正如Joshua Bloch在 Effective Java 中所说

PRIVATE CONSTRUCTORS - 它们在像 Singleton 这样的模式中有自己的用途,但也有自己的局限性。

于 2012-11-30T17:36:13.980 回答
1

我自己在Effective Java Item 15 中的一些发现,粘贴了相同的相关语句

“回想一下,为了保证不可变性,一个类不能允许自己被子类化。通常这是通过将类设为 final 来完成的,但还有另一种更灵活的方法可以做到这一点。将不可变类设为 final 的替代方法是让它的所有构造函数都是私有的或包私有的,并添加公共静态工厂来代替公共构造函数(第 1 项)。

虽然这种方法并不常用,但它通常是最好的选择。它是最灵活的,因为它允许使用多个包私有实现类。对于位于其包外的客户而言,不可变类实际上是最终类,因为无法扩展来自另一个包且缺少公共或受保护构造函数的类。除了允许多个实现类的灵活性之外,这种方法还可以通过改进静态工厂的对象缓存能力在后续版本中调整类的性能。

"

然后讨论静态工厂与构造函数的优势

于 2012-11-30T17:50:32.290 回答
0

那是因为使用私有构造函数我们不能创建它的子类,因此限制了任何其他扩展它的类的所有路径。它很复杂,因为它有自己的局限性/复杂性,比如单例模式。

于 2012-11-30T17:08:55.360 回答
0

私有构造函数背后的想法是,您希望隐藏类数据的不可变实现,同时允许构造具有相同内部类型的不同数据的新实例。

例如

public class Temperature
{
    private readonly double temperatureInCelsius;

    public Temperature(string temperatureInCelsius)
    {
         this.temperatureInCelsius = System.Convert.ToDouble(temperatureInCelsius);
    }
    private Temperature(double temperatureInCelsius)
    {
         this.temperatureInCelsius = temperatureInCelsius;
    }

    public Temperature AddCelsius(string temperatureToAddInCelsius)
    {
         return new Temperature(System.Convert.ToDouble(temperatureToAddInCelsius) + temperatureInCelsius);
    }
    public void PrintCelsius(Display display)
    {
        display.Print(temperatureInCelsius);
    }
    public void PrintFarenheit(Display display)
    // ... etc
}

忽略示例的半愚蠢,如果您的要求是可以从表示温度的字符串构造类。它实际存储温度的方式可能会发生变化,并且是一个实现细节。可以将此类更改为使用浮点数、字符串、双精度数、整数等。此方法保持不变性,同时允许实现灵活性。显然,当您包装更复杂的对象(如集合、字典等)时,这会变得更加强大。

于 2013-01-31T18:46:59.363 回答