返回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方法,这个消息将是相同的。
看看下面的文章:
安迪吉布森