返回ImmutableMap还是Map更好?

假设我正在写一个应该返回一个Map的方法 。 例如:

public Map<String, Integer> foo() { return new HashMap<String, Integer>(); } 

经过一段时间的思考,我已经决定,一旦创build了这个地图,就没有理由去修改它。 因此,我想返回一个ImmutableMap 。

 public Map<String, Integer> foo() { return ImmutableMap.of(); } 

我应该离开返回types作为一个通用的地图,或者我应该指定我返回一个ImmutableMap?

从一方面来说,这正是为什么界面被创build的原因。 隐藏实现细节。
另一方面,如果我这样离开它,其他开发人员可能会错过这个对象是不可改变的事实。 因此,我不会实现不变的客体的主要目标; 通过最小化可以改变的对象的数量来使得代码更清楚。 甚至最糟糕的是,过了一段时间,有人可能会尝试更改此对象,这将导致运行时错误(编译器不会警告它)。

  • 如果你正在编写一个面向公众的API,而且不变性是你devise的一个重要方面,那么我肯定会明确地说明它,方法是用方法的名称清楚地表示返回的映射是不可变的,或者返回具体的types地图。 在我看来,在javadoc中提到这是不够的。

    由于你明显使用Guava实现,所以我查看了文档,它是一个抽象类,所以它给你一些实际的具体types的灵活性。

  • 如果你正在编写一个内部的工具/库,只要返回一个简单的Map就变得更加可以接受了。 人们会知道他们正在调用的代码的内部,或者至less可以轻松访问它。

我的结论是明确的是好的,不要把事情弄糟。

你应该有ImmutableMap作为你的返回types。 Map包含ImmutableMap实现不支持的方法(例如put ),并在ImmutableMap中标记@deprecated

使用不推荐使用的方法将导致编译器警告,大多数IDE会在人们尝试使用已弃用的方法时发出警告。

这个先进的警告比作为你的第一个暗示有什么错误的运行时exception更可取。

另一方面,如果我这样离开它,其他开发人员可能会错过这个对象是不可改变的事实。

你应该在javadocs中提到。 开发者确实阅读了他们,你知道。

因此,我不会实现不变的客体的主要目标; 通过最小化可以改变的对象的数量来使得代码更清楚。 甚至最糟糕的是,过了一段时间,有人可能会尝试更改此对象,这将导致运行时错误(编译器不会警告它)。

没有开发者发布他的代码未经testing。 当他testing的时候,他得到了一个抛出的Exception,他不仅看到了原因,而且还试图写入一个不可改变的地图的文件和行。

但请注意,只有Map本身将是不可变的,而不是它包含的对象。

如果我像这样离开,其他开发人员可能会错过这个对象是不可变的事实

这是真的,但其他开发人员应该testing他们的代码,并确保它被覆盖。

尽pipe如此,你还有两个select来解决这个问题:

  • 使用Javadoc

     @return a immutable map 
  • select一个描述性的方法名称

     public Map<String, Integer> getImmutableMap() public Map<String, Integer> getUnmodifiableEntries() 

    对于一个具体的用例,你甚至可以更好地命名这些方法。 例如

     public Map<String, Integer> getUnmodifiableCountByWords() 

你还能做什么?!

你可以返回一个

  • 复制

     private Map<String, Integer> myMap; public Map<String, Integer> foo() { return new HashMap<String, Integer>(myMap); } 

    如果你期望有很多客户会修改地图,只要地图只包含几个条目,就应该使用这种方法。

  • CopyOnWriteMap

    当你必须处理的时候,通常使用写集合的副本
    并发性。 但是这个概念也可以帮助你处理你的情况,因为一个CopyOnWriteMap在一个可变操作(例如add,remove)上创build一个内部数据结构的副本。

    在这种情况下,除了变化操作之外,您需要在地图上包装一个薄的包装器,将所有的方法调用委托给底层的地图。 如果调用一个可变操作,它会创build一个底层映射的副本,所有进一步的调用将被委托给这个副本。

    如果您希望某些客户端将修改地图,则应使用此方法。

    可悲的是,java没有这样的CopyOnWriteMap 。 但你可能会find第三方或自己实施。

最后你应该记住,地图中的元素可能仍然是可变的。

肯定返回ImmutableMap,理由是:

  • 方法签名(包括返回types)应该是自行logging的。 评论就像客户服务:如果你的客户需要依靠他们,那么你的主要产品是有缺陷的。
  • 无论是接口还是类,只有在扩展或实现它时才有意义。 给定一个实例(对象),99%的时间客户端代码将不知道或关心是否是接口或类。 我首先假定ImmutableMap是一个接口。 只有当我点击链接后,我才意识到这是一个类。

这取决于class级本身。 番石榴的ImmutableMap并不打算成为一个可变类的不可变视图。 如果你的类是不可变的,并且有一些基本上是ImmutableMap结构,那么使返回typesImmutableMap 。 但是,如果你的课是可变的,不要。 如果你有这个:

 public ImmutableMap<String, Integer> foo() { return ImmutableMap.copyOf(internalMap); } 

番石榴会每次都复制地图。 这很慢。 但是如果internalMap已经是ImmutableMap ,那就完全没问题了。

如果你不限制你的类返回ImmutableMap ,那么你可以像下面这样返回Collections.unmodifiableMap

 public Map<String, Integer> foo() { return Collections.unmodifiableMap(internalMap); } 

请注意,这是一个不可变的地图视图 。 如果internalMap更改,那么Collections.unmodifiableMap(internalMap)的caching副本也将更改。 但是我仍然喜欢吸气剂。

这并没有回答确切的问题,但仍然值得考虑是否应该返回地图。 如果映射是不可变的,那么要提供的主要方法是基于get(key):

 public Integer fooOf(String key) { return map.get(key); } 

这使得API更加紧密。 如果实际需要映射,则可以通过提供条目stream将其留给API的客户端:

 public Stream<Map.Entry<String, Integer>> foos() { map.entrySet().stream() } 

然后,客户端可以根据需要制作自己的不可变或可变的地图,或者将条目添加到自己的地图中。 如果客户端需要知道该值是否存在,可以select返回:

 public Optional<Integer> fooOf(String key) { return Optional.ofNullable(map.get(key)); } 

Immutable Map是一种Map。 所以留下返回types的地图是可以的。

为了确保用户不修改返回的对象,方法的文档可以描述返回对象的特性。

这可以说是一个意见问题,但更好的想法是使用地图类的接口。 这个接口不需要明确地说它是不可变的,但是如果你不在接口中暴露父类的任何setter方法,这个消息将是相同的。

看看下面的文章:

安迪吉布森

Interesting Posts