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