1

我多次遇到类似的代码:

class AClass{
    private Iterable<String> list;
    public AClass(Iterable<String> list){ this.list = list; }
    ...
}

在这段代码中,Iterable 的引用直接传递给 AClass。最终结果相当于直接将列表引用暴露给外部。即使您将 AClass.list 设为 final,它仍然允许来自 AClass 外部的代码修改列表的内容,这很糟糕。

为了解决这个问题,我们将在构造函数中进行防御性复制。

但是,这种代码很常见。除了性能考虑之外,人们编写这种代码的意图是什么?

4

5 回答 5

1

简单的回答,如果是你自己的代码/小团队,以这种方式做事通常更快、更容易、更少的内存和 CPU 密集型。而且,有些人就是不知道更好!

于 2012-12-12T06:44:04.457 回答
1

我看不出这种模式有什么问题。如果该类表示对列表(或可迭代)进行操作的对象,那么将该列表提供给构造函数是很自然的。如果您的类无法处理对基础集合的更改,则需要对其进行修复或记录。制作收藏品的副本是解决此问题的一种方法。

另一种选择是更改接口,以便只允许不可变集合:

public AClass(ImmutableList<MyObject> objects) {
    this.objects = objects;
    ...

当然,您需要某种 ImmutableList 类或接口。

根据您的课程的用途和用户,您还可以通过记录已知的“弱点”来避免复制:

/**
 * ...
 * @param objects list of objects this AClass-object operates on.
 *                The list should not be modified during the lifetime 
 *                of this object
 */
public AClass(List<MyObject> objects) ...
于 2012-12-12T09:30:42.937 回答
1

您可能想查看一个熟悉的习语的复制构造函数。

于 2012-12-12T07:01:47.953 回答
0

如果代码在内部使用,正如其他答案所指出的那样,那应该不是问题。但是,如果您要公开为 API,那么有两种选择:

首先是创建一个防御副本,然后将其返回

其次是创建一个UnmodifiableCollection然后返回它并记录尝试更改集合中的任何内容可能导致异常的事实。

但第一种选择更可取。

于 2012-12-12T07:50:36.710 回答
0

制作副本总是很好的做法,不仅因为其他人可以修改您的值,而且出于安全原因。

于 2012-12-12T06:49:56.980 回答