首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么要在不可变类中的getter中进行防御性复制呢?

为什么要在不可变类中的getter中进行防御性复制呢?
EN

Stack Overflow用户
提问于 2012-06-08 23:32:44
回答 7查看 11.6K关注 0票数 18

这个问题是关于良好的编程实践和避免潜在的漏洞。

我读了Joshua Bloch的Effective,下面是我想知道的:

为什么我要考虑在我的不可变类中的getter方法中进行防御性复制,而不是在其中没有赋值函数?

第二:除了private之外,我为什么要让我的字段成为final?这仅仅是关于性能(而不是安全性)吗?

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2012-06-08 23:37:05

我相信这就是证明这一说法的理由:

public class Immutable {

    private final String name;

    private Date dateOfBirth;

    public Immutable(String name, Date dateOfBirth) {
        this.name = name;
        this.dateOfBirth = dateOfBirth;
    }

    public String getName() {
        return name;
    }

    public Date getDateOfBirth() {
        return dateOfBirth;
    }

}

getName()很好,因为它也返回不可变的对象。但是,getDateOfBirth()方法可以打破不变性,因为客户端代码可以修改返回的对象,因此也可以修改Immutable对象:

Immutable imm = new Immutable("John", new Date());

imm.getName(); //safe
Date dateOfBirth = imm.getDateOfBirth();
//hundreds of lines later
dateOfBirth.setTime(0);  //we just modified `imm` object

返回不可变对象和原语是安全的(因为它们是通过值返回的)。但是,您需要创建可变对象的防御性副本,如Date

public Date getDateOfBirth() {
    return new Date(dateOfBirth.getTime());
}

并将集合包装在不可变的视图中(如果它们是可变的),例如参见Collections.unmodifiableList()

public List<Integer> getSomeIds() {
    return Collections.unmodifiableList(someIds);
}
票数 41
EN

Stack Overflow用户

发布于 2012-06-13 00:41:23

Java中的对象引用是混杂的;如果一个对象引用被传递给一个方法,那么该方法将无法知道还有谁可能拥有对该对象的引用,或者同样地,他们可能会尝试用它来做什么。同样,如果一个方法返回一个对象引用,也不知道接收者会对它做什么。因此,如果类型允许任何具有引用的人对其进行更改,则持有对此类对象的引用的实体可以确保其不会被更改的唯一方法是保留对私有对象的引用,而外部从来没有,也永远不会获得对该对象的引用。

这里所示的防御性复制的另一种方式(每次请求信息时构造一个新的对象实例)是让对象的信息检索方法接受一个可变对象,并用检索到的信息填充该对象。这种方法要求请求信息的代码在调用检索信息的方法之前构造一个(可能是空白的)对象来接受该信息,但是如果将例如使用循环来检索、简要地检查和丢弃100条信息,则构造一个每次都可以通过该循环重用的对象可能比构造100个新对象更快,每个新对象中的每一个都将被短暂地使用。

票数 4
EN

Stack Overflow用户

发布于 2012-06-08 23:38:05

虽然封闭的类可能是不可变的,但它的getter方法返回的引用可能是可变的,从而允许调用者修改不可变的传递状态。示例:

public class MyImmutable
{
  private final StringBuilder foo = new StringBuilder("bar");

  public StringBuilder getFoo()
  {
    return foo; // BAD!
  }
}

您应该使用私有进行封装(防止类依赖于您的类的实现细节)。你应该使用final来确保你不会错误地修改这个字段,如果它不打算被修改的话(是的,它可能有助于提高性能)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10951820

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档