Effective Java的防御性副本

Lon*_*man 19 java design-patterns

我正在阅读约书亚布洛赫的"有效Java",第39项制作防御性副本,我有一些问题.我总是使用以下构造:

MyObject.getSomeRef().setSomething(somevalue);
Run Code Online (Sandbox Code Playgroud)

这是短的:

SomeRef s = MyClass.getSomeRef();
s.setSomething();
MyObject.setSomeRef(s);
Run Code Online (Sandbox Code Playgroud)

它总是有效,但我想如果我getSomeRef()正在返回副本,那么我的快捷方式将不起作用,MyObject如果使用快捷方式是否安全,我怎么知道隐藏的实现是否隐藏?

JB *_*zet 27

你违反了两个OO编程规则:

  • 不要和陌生人说话
  • 封装

请注意,这些规则只是规则,有时它们可​​以甚至必须被破坏.

但是,如果某个数据由一个对象拥有,并且该对象应该保证它拥有的对象上有一些不变量,那么它就不应该将其可变内部数据结构暴露给外部.因此需要一个防御性的副本.

另一个常用的习惯用法是返回可变数据结构的不可修改的视图:

public List<Foo> getFoos() {
    return Collections.unmodifiableList(this.foos);
}
Run Code Online (Sandbox Code Playgroud)

例如,如果您必须确保对列表的每个修改都通过对象,那么这个成语或防御性复制惯用语可能很重要:

public void addFoo(Foo foo) {
    this.foos.add(foo);
    someListener.fooAsBeenAdded(foo);
}
Run Code Online (Sandbox Code Playgroud)

如果你不防守副本或返回列表的不可修改视图,呼叫者可以直接在富添加到列表中,而听者将不会被调用.

  • 通过调用 `addFoo()` 方法。 (2认同)

Mar*_*ers 10

文档是您(应该)知道的方式. MyObject应该记录它所暴露的东西是否可以或应该用来修改MyObject自己. 您应该只以类明确授予的方式修改对象.

例如,以下是两个方法的Javadoc List,其中一个结果不能用于更改List,一个结果可以更改List:

toArray():

返回的数组将是"安全的",因为此列表不会保留对它的引用.(换句话说,即使此列表由数组支持,此方法也必须分配新数组).因此调用者可以自由修改返回的数组.

subList():

返回的列表由此列表支持,因此返回列表中的非结构更改将反映在此列表中,反之亦然.返回的列表支持此列表支持的所有可选列表操作.

我会说文档中的沉默意味着你不应该使用它来改变对象(仅用于只读目的).