从参数中分配集合的首选方法是什么?

我有这堂课:

public MyClass { public void initialize(Collection data) { this.data = data; // <-- Bad! } private Collection data; } 

这显然是糟糕的风格,因为我引入了一个共享的可变状态。 处理这个问题的首选方法是什么?

  • 忽略它?
  • 克隆collections?
  • …?

编辑:为了澄清为什么这是坏的,想象一下:

 MyClass myObject = new MyClass(); List data = new ArrayList(); myObject.initialize(data); // myObject.data.size() == 0 data.add("Test"); // myObject.data.size() == 1 

只是存储引用构成了一种将数据注入私有字段myObject.data ,尽管它应该是完全私有的。

根据MyClass的性质,这可能会产生严重影响。

最好的方法是深度克隆参数。 出于性能原因,这通常是不可能的。 最重要的是,并非所有对象都可以克隆,因此深度复制可能会引发exception并导致各种头痛。

下一个最好的方法是“写时复制”克隆。 Java运行时中不支持此function。

如果您认为有人可能会改变集合,请使用复制构造函数执行浅表复制:

 this.data = new HashSet (data); 

这将解决您的问题(因为String是不可变的)但是当集合中的类型是可变的时它将失败。

另一种解决方案是,只要将它们存储在某处,就始终使这些集不可变:

 Set set = ... ...build the set... // Freeze the set set = Collections.unmodifiableSet(set); // Now you can safely pass it elsewhere obj.setData (set); 

这里的想法是尽快将集合转换为“价值对象”。 任何想要更改集合的人都必须复制它,更改它然后再保存。

在一个类中,你可以保持set可变并将它包装在getter中(无论如何你都应该这样做)。

这种方法存在的问题:性能(但可能没有你期望的那么糟糕)和纪律(如果你忘了它就会​​中断)。

  • 空检查(如果要限制null)
  • 防御副本(如果你不想要共享状态)
  • 或者像你一样(如果数据的实时视图有用)

在很大程度上取决于您的要求。

编辑:忽略应该是没有选择。 沉默的失败是,……一个调试的噩梦。

 public class Foo { private final Collection collection = new ArrayList(); public void initialise(final Collection collection) { this.collection.addAll(collection); } } 

很抱歉没有直接解决您的问题,但我永远不会直接将Collection传递给setXxx()bean setter方法。 相反,我会这样做:

 private final List theList; public void addXxx(MyClass item) { ... } public void removeXxx(MyClass item) { ... } // or index. public void Iterator iterateXxx() { return Collections.unmodifiableList(theList).iterator(); } 

只有当我确定使用它没有副作用时,我会选择防御性复制/深度克隆,至于速度,我不会关心它,因为在商业应用中,可靠性的优先级是10倍。速度。 😉

一个想法是将数据作为String数组传递并在MyClass中创建Set。 当然,MyClass应该测试输入数据是否有效。 无论如何,我相信这是一个很好的做法。

如果MyClass和MyClass本身的调用者实际上都使用Set ,那么您可以考虑克隆该集合。 然而,集合需要以某种方式构建。 我更愿意将此责任移交给MyClass。