是否应该制作防御性副本?

时间:2013-08-09 06:33:56

标签: java

我有一个代码,它以二分图作为输入,返回一个带键“1”的映射,其值为“set1”中的节点和键“2”的列表,其值为“set2中的节点”列表”。现在,地图是可变的。从理论上讲,我应该使用防御性副本来返回地图。但是,在这种情况下它真的需要吗?这似乎有点矫枉过正。

例如:

class BiPartite {

   Graph graph;
   Map bipartite

   Bipartite(graph) {
      this.graph = graph;
   }

   void calcBipartite() {
     // calculate map 
   }

   Map getMap() {
     // should i make defensive copy ? Appears overkill. 
   }  
}

6 个答案:

答案 0 :(得分:6)

取决于:)

  • 您可以相应地记录您的类,指定返回的Map是BiPartite对象的可变状态的可变直接视图。但是,我不建议这样做。
  • 您可以使用Collections.unmodifiableMap(map) 文档中的getMap()封装内部地图,以反映BiPartite对象的可变状态。只要BiPartite对象不应该是线程安全的,那么这可能是一种有效的方法。如果客户想要将返回的Map保持为稳定的快照,她可以自己复制它。如果不需要,她可以从快速包装操作中受益。
  • 您随时可以返回完整副本。如果你真的使BiPartite对象线程安全,那将是最有意义的。在这种情况下,您还必须同步内部地图的所有操作(包括地图复制操作!)。

基本上归结为:考虑如何使用BiPartite类及其方法,为此选择合适的实现并清楚地记录类的行为及其背后的推理。

答案 1 :(得分:5)

是的,你应该返回一份防御性副本。如果您担心资源使用,可以返回一张不可修改的私人地图视图:

return Collections.unmodifiableMap(bipartite);

答案 2 :(得分:2)

在解释模式和良好的编程实践时,“总是”这个词很少适用。这一切都取决于具体情况。

在您的情况下,BiPartite#getMap方法和类本身是“包私有”,因此客户端(代码的用户)将无法直接使用它。如果你知道你从未在该套餐的边界之外存储或返回该地图,那么可以很自然地说你不需要制作防御性副本。

答案 3 :(得分:1)

是的,你应该, 因为否则客户可以更改班级的私人领域,使你的班级表现错误。

答案 4 :(得分:1)

这取决于代码的约定。我按照惯例复制你想要保留的任何内容。即是来电者的责任。

这样效率更高,但如果您不知道调用者将遵循此约定,则效果不佳。

答案 5 :(得分:0)

防御性拷贝是一件坏事,因为我遇到的大多数java框架都希望getter能够始终返回相同的值并快速完成。

如果你想从你的代码的行为不端的客户端中保存,你应该只暴露方法calcBipartite()并让它返回一个新计算的Map。使用该方法的客户端必须决定如何使用创建的对象以及调用calcBipartite()的次数。

如果您是代码的唯一客户,则不应复制或包装。