为什么我们必须对不可变进行防御性复制 类
Why we must do defensive copying for imutable classes
为什么我们必须进行防御性复制才能实现不可变class?
看这段代码:
public final class EmailMessage {
private final String from;
private final String to;
private final String message;
private final Date date;
public EmailMessage( String from, String to, String msg, Date date )
{
this.to = to;
this.from = from;
this.message = msg;
this.date = new Date(date.getTime());// instead of date;
}
public String getFrom()
{
return( from );
}
public Date getDate() {
return( new Date( date.getTime() ); // instead of Date
}
}
如果我们不进行防御性复制,为什么它不会是不可变的?
为了实现不变性,您必须复制所有传递给构造函数的可变对象,并且还 return 存储在您的 class 中的所有可变对象的副本,如果有的话。
- 如果您不复制传递给您的日期,调用者可以在您的对象构建后更改日期,从而有效地改变它。
- 如果您没有 return 来自可变对象 getter 的副本,调用者可以改变他们从您那里获得的对象,从而有效地改变您的对象。
在您的具体示例中,Date
class 是可变的。如果你跳过构造函数中的复制,恶意代码可以这样做:
Date d = new ...
EmailMessage msg = new EmailMessage("lazy dog", "quick brown fox", "Jump!", d);
d.setTime(d.getTime()+12345); // Changes the date inside msg
如果跳过第二个副本,调用者可以这样做:
EmailMessage msg = ...
Date d = msg.getDate();
d.setTime(d.getTime()+12345); // Changes the date inside msg
因为否则可以更改对象状态。让我们想象一下,您的 getDate
方法如下:
public Date getDate() {
return date; // instead of Date
}
而我们的使用方式如下:
EmailMessage msg = new EmailMessage(...); // initialization
Date date = msg.getDate();
date.setTime(...); //ooops, our msg object has another date now
为什么我们必须进行防御性复制才能实现不可变class? 看这段代码:
public final class EmailMessage {
private final String from;
private final String to;
private final String message;
private final Date date;
public EmailMessage( String from, String to, String msg, Date date )
{
this.to = to;
this.from = from;
this.message = msg;
this.date = new Date(date.getTime());// instead of date;
}
public String getFrom()
{
return( from );
}
public Date getDate() {
return( new Date( date.getTime() ); // instead of Date
}
}
如果我们不进行防御性复制,为什么它不会是不可变的?
为了实现不变性,您必须复制所有传递给构造函数的可变对象,并且还 return 存储在您的 class 中的所有可变对象的副本,如果有的话。
- 如果您不复制传递给您的日期,调用者可以在您的对象构建后更改日期,从而有效地改变它。
- 如果您没有 return 来自可变对象 getter 的副本,调用者可以改变他们从您那里获得的对象,从而有效地改变您的对象。
在您的具体示例中,Date
class 是可变的。如果你跳过构造函数中的复制,恶意代码可以这样做:
Date d = new ...
EmailMessage msg = new EmailMessage("lazy dog", "quick brown fox", "Jump!", d);
d.setTime(d.getTime()+12345); // Changes the date inside msg
如果跳过第二个副本,调用者可以这样做:
EmailMessage msg = ...
Date d = msg.getDate();
d.setTime(d.getTime()+12345); // Changes the date inside msg
因为否则可以更改对象状态。让我们想象一下,您的 getDate
方法如下:
public Date getDate() {
return date; // instead of Date
}
而我们的使用方式如下:
EmailMessage msg = new EmailMessage(...); // initialization
Date date = msg.getDate();
date.setTime(...); //ooops, our msg object has another date now