Java 关于不变模式的几个问题

Java 关于不变模式的几个问题,java,design-patterns,Java,Design Patterns,我有一些关于不可变模式的问题。首先,为了解决问题,我们需要确保以下几点: 1) 访问属性的实例方法不能更改实例变量 2) 确保不可变类的构造函数是 设置或修改实例变量的值。 我认为,通过将inctance变量作为最终变量,那么如果存在一个改变这个变量的方法将会失败,即使构造函数也无法改变这个变量,那么如果我们可以使用final关键字,为什么我们需要考虑这两个语句呢??我知道我遗漏了一些东西。有一件事很重要,那就是使真正的不可变。这些组件可能是可变的数据结构,例如java.util.Date。以这

我有一些关于不可变模式的问题。首先,为了解决问题,我们需要确保以下几点:

1) 访问属性的实例方法不能更改实例变量

2) 确保不可变类的构造函数是 设置或修改实例变量的值。
我认为,通过将inctance变量作为最终变量,那么如果存在一个改变这个变量的方法将会失败,即使构造函数也无法改变这个变量,那么如果我们可以使用final关键字,为什么我们需要考虑这两个语句呢??我知道我遗漏了一些东西。

有一件事很重要,那就是使真正的不可变。这些组件可能是可变的数据结构,例如
java.util.Date
。以这个简单的类为例进行演示:

final class DateWrapper {
  private final Date date;
  public DateWrapper(Date date) {
    this.date = date;
  }
  public Date getDate() {
    return date;
  }
}
现在您可以编写以下代码:

DateWrapper dw = new DateWrapper(new Date());
dw.getDate().setYear(2020);
这将修改类
DateWrapper
的对象
dw
的实例变量
date
。所以这个类并不是真的不可变的

要解决此问题,您必须制作如下防御性副本:

final class DateWrapper {
  private final Date date;
  public DateWrapper(Date date) {
    // It must not be possible to modify the date after construction.
    // So use a copy of the original object in class DateWrapper.
    this.date = new Date(date.getTime());
  }
  public Date getDate() {
    // It must not be possible to modify the date in DateWarapper
    // So return a copy.
    return new Date(date.getTime());
  }
}
因此,没有其他类会获得对
DateWrapper
内部的可变引用

另一条规则是:将类设为final,否则子类可以作为不可变类,从而打破不可变性的契约。这是恶意子类的一个示例,仅当DateWrapper不是final时才有效:

class Date2Wrapper extends DateWrapper {
  final Date date2;
  public Date2Wrapper(Date date) {
    super(date);
    this.date2 = date;
  }
  public Date getDate() {
    return date2;
  }
}

首先,设计模式并不局限于java。它们可以用不同的语言实现,具有不同的风格。并非所有语言都有期末考试,甚至java从一开始就没有期末考试

但考虑到java的严格情况,final并不能确保不变性:

  • 在对象中,如果可以访问成员的方法,则可以使用这些方法更改状态
  • 应用于对象的final可确保变量指向同一对象,但不会设置任何限制以避免更改状态(ball.setColor(“红色”)可在final ball上执行)
  • 不变性意味着一个变量应该可以重新分配给另一个对象
例如:

Ball ball = new Ball(); 
ball = ball.makeRed();
在这种情况下,球是另一个球对象(使球克隆球并更改颜色)。当我们在String对象上运行方法时,也会发生同样的情况,创建一个新的String对象以反映更改并返回:

String ball = 'Blue Ball';
ball = ball.replace('Blue', 'Red');

为了解决您描述的问题,您不仅在
getDate
方法中创建了防御副本,而且在构造函数中也创建了防御副本。这是因为传递给构造函数的引用可能会在外部更改,从而使您的类也可变。@Hannes看起来他正在使用传入的Date对象中的值在构造函数中实例化一个新的Date对象,从而保护类本身不受对该引用所做更改的影响Yes,还需要在构造函数中创建防御副本。我在课文中没有明确提到这一点。我现在已经在java代码的注释中包含了这些信息。@Donat-您认为
final static class DateWrapper
中的
static
有什么作用?@Donat-np。如果
DateWrapper
不是final,你能解释一下
DateWrapper
的子类如何访问内部吗?你能提供一个简单的例子,通过setter方法我们可以在初始化后更改不可变对象的状态吗?