java 返回 ImmutableMap 或 Map 哪个更好?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38087900/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it better to return an ImmutableMap or a Map?
提问by AMT
Let's say I'm writing a method that should return a Map. For instance:
假设我正在编写一个应该返回Map 的方法。例如:
public Map<String, Integer> foo() {
return new HashMap<String, Integer>();
}
After thinking about it for a while, I've decided that there is no reason to modify this Map once it is created. Thus, I would like to return an ImmutableMap.
考虑了一段时间后,我决定一旦创建了这个 Map 就没有理由修改它。因此,我想返回一个ImmutableMap。
public Map<String, Integer> foo() {
return ImmutableMap.of();
}
Should I leave the return type as a generic Map, or should I specify that I'm returning an ImmutableMap ?
From one side, this is exactly why interfaces were created for; to hide the implementation details.
On the other hand, if I'll leave it like this, other developers might miss the fact that this object is immutable. Thus, I won't achieve a major goal of immutable objects; to make the code more clear by minimizing the number of objects that can change. Even worst, after a while, someone might try to change this object, and this will result in a runtime error (The compiler will not warn about it).
我应该将返回类型保留为通用 Map ,还是应该指定我要返回 ImmutableMap ?
一方面,这正是创建接口的原因;隐藏实现细节。
另一方面,如果我就这样保留它,其他开发人员可能会错过这个对象是不可变的这一事实。因此,我不会实现不可变对象的主要目标;通过最小化可以更改的对象数量来使代码更加清晰。更糟糕的是,一段时间后,有人可能会尝试更改此对象,这将导致运行时错误(编译器不会对此发出警告)。
采纳答案by Dici
If you are writing a public-facing API and that immutability is an important aspect of your design, I would definitely make it explicit either by having the name of the method clearly denotes that the returned map will be immutable or by returning the concrete type of the map. Mentioning it in the javadoc is not enough in my opinion.
Since you're apparently using the Guava implementation, I looked at the doc and it's an abstract class so it does give you a bit of flexibility on the actual, concrete type.
If you are writing an internal tool/library, it becomes much more acceptable to just return a plain
Map
. People will know about the internals of the code they are calling or at least will have easy access to it.
如果您正在编写面向公众的 API 并且不变性是您设计的一个重要方面,我肯定会通过让方法名称明确表示返回的映射将是不可变的或通过返回具体类型地图。在我看来,在 javadoc 中提及它是不够的。
由于您显然在使用 Guava 实现,因此我查看了文档,它是一个抽象类,因此它确实为您提供了实际的具体类型的灵活性。
如果您正在编写内部工具/库,那么只返回一个普通的
Map
. 人们会知道他们正在调用的代码的内部结构,或者至少可以轻松访问它。
My conclusion would be that explicit is good, don't leave things to chance.
我的结论是明确是好的,不要让事情碰运气。
回答by Synesso
You should have ImmutableMap
as your return type. Map
contains methods that are not supported by the implementation of ImmutableMap
(e.g. put
) and are marked @deprecated
in ImmutableMap
.
你应该有ImmutableMap
作为你的返回类型。Map
包含ImmutableMap
(eg put
)的实现不支持并在 中标记@deprecated
的方法ImmutableMap
。
Using deprecated methods will result in a compiler warning & most IDEs will warn when people attempt to use the deprecated methods.
使用不推荐使用的方法会导致编译器警告,当人们尝试使用不推荐使用的方法时,大多数 IDE 会发出警告。
This advanced warning is preferable to having runtime exceptions as your first hint that something is wrong.
此高级警告比将运行时异常作为第一个提示错误的提示更可取。
回答by tkausl
On the other hand, if I'll leave it like this, other developers might miss the fact that this object is immutable.
另一方面,如果我就这样保留它,其他开发人员可能会错过这个对象是不可变的这一事实。
You should mention that in the javadocs. Developers do read them, you know.
您应该在 javadocs 中提到这一点。开发人员会阅读它们,你知道的。
Thus, I won't achieve a major goal of immutable objects; to make the code more clear by minimizing the number of objects that can change. Even worst, after a while, someone might try to change this object, and this will result in a runtime error (The compiler will not warn about it).
因此,我不会实现不可变对象的主要目标;通过最小化可以更改的对象数量来使代码更加清晰。更糟糕的是,一段时间后,有人可能会尝试更改此对象,这将导致运行时错误(编译器不会对此发出警告)。
No developer publishes his code untested. And when he does test it, he gets an Exception thrown where he not only sees the reason but also the file and line where he tried to write to an immutable map.
没有开发人员发布未经测试的代码。当他测试它时,他会抛出一个异常,他不仅可以看到原因,还可以看到他试图写入不可变映射的文件和行。
Do note though, only the Map
itself will be immutable, not the objects it contains.
但请注意,只有它Map
本身是不可变的,而不是它包含的对象。
回答by René Link
if I'll leave it like this, other developers might miss the fact that this object is immutable
如果我就这样保留它,其他开发人员可能会错过这个对象不可变的事实
That's true, but other developers should test their code and ensure that it is covered.
这是真的,但其他开发人员应该测试他们的代码并确保它被覆盖。
Nevertheless you have 2 more options to solve this:
尽管如此,您还有 2 个选择来解决这个问题:
Use Javadoc
@return a immutable map
Chose a descriptive method name
public Map<String, Integer> getImmutableMap() public Map<String, Integer> getUnmodifiableEntries()
For a concrete use case you can even name the methods better. E.g.
public Map<String, Integer> getUnmodifiableCountByWords()
使用 Javadoc
@return a immutable map
选择一个描述性的方法名称
public Map<String, Integer> getImmutableMap() public Map<String, Integer> getUnmodifiableEntries()
对于具体用例,您甚至可以更好地命名方法。例如
public Map<String, Integer> getUnmodifiableCountByWords()
What else can you do?!
你还能做什么?!
You can return a
你可以返回一个
copy
private Map<String, Integer> myMap; public Map<String, Integer> foo() { return new HashMap<String, Integer>(myMap); }
This approach should be used if you expect that a lot of clients will modify the map and as long as the map only contains a few entries.
CopyOnWriteMap
copy on write collections are usually used when you have to deal with
concurrency. But the concept will also help you in your situation, since a CopyOnWriteMap creates a copy of the internal data structure on a mutative operation (e.g. add, remove).In this case you need a thin wrapper around your map that delegates all method invocations to the underlying map, except the mutative operations. If a mutative operation is invoked it creates a copy of the underlying map and all further invocations will be delegated to this copy.
This approach should be used if you expect that some clients will modify the map.
Sadly java does not have such a
CopyOnWriteMap
. But you might find a third party or implement it by yourself.
复制
private Map<String, Integer> myMap; public Map<String, Integer> foo() { return new HashMap<String, Integer>(myMap); }
如果您预计很多客户端会修改地图并且只要地图只包含几个条目,就应该使用这种方法。
CopyOnWriteMap
当您必须处理
并发时,通常会使用写时复制集合。但是这个概念也会在您的情况下帮助您,因为 CopyOnWriteMap 在可变操作(例如添加、删除)上创建内部数据结构的副本。在这种情况下,您需要一个围绕您的地图的瘦包装器,将所有方法调用委托给底层地图,但可变操作除外。如果调用了一个可变操作,它会创建一个底层映射的副本,并且所有进一步的调用都将委托给这个副本。
如果您预计某些客户端会修改地图,则应使用此方法。
遗憾的是,java 没有这样的
CopyOnWriteMap
. 但您可能会找到第三方或自行实施。
At last you should keep in mind that the elements in your map might still be mutable.
最后,您应该记住地图中的元素可能仍然是可变的。
回答by Benito Ciaro
Definitely return an ImmutableMap, justification being:
绝对返回一个 ImmutableMap,理由是:
- The method signature (including return type) should be self-documenting. Comments are like customer service: if your clients need to rely on them, then your primary product is defective.
- Whether something is an interface or a class is only relevant when extending or implementing it. Given an instance (object), 99% of the time client code will not know or care whether something is an interface or a class. I assumed at first that ImmutableMap was an interface. Only after I clicked the link did I realize it is a class.
- 方法签名(包括返回类型)应该是自记录的。评论就像客户服务:如果您的客户需要依赖他们,那么您的主要产品就有缺陷。
- 某个东西是接口还是类仅在扩展或实现它时才相关。给定一个实例(对象),99% 的时间客户端代码不会知道或关心某个东西是接口还是类。起初我认为 ImmutableMap 是一个接口。只有在我点击链接后,我才意识到这是一个类。
回答by Justin
It depends on the class itself. Guava's ImmutableMap
isn't intended to be an immutable view into a mutable class. If your class is immutable and has some structure that is basically an ImmutableMap
, then make the return type ImmutableMap
. However, if your class is mutable, don't. If you have this:
这取决于类本身。GuavaImmutableMap
并不是要成为可变类的不变视图。如果您的类是不可变的并且具有一些基本上是 an 的结构ImmutableMap
,那么请设置返回类型ImmutableMap
。但是,如果您的类是可变的,请不要。如果你有这个:
public ImmutableMap<String, Integer> foo() {
return ImmutableMap.copyOf(internalMap);
}
Guava will copy the map every time. That's slow. But if internalMap
was already an ImmutableMap
, then it's totally fine.
Guava 每次都会复制地图。那很慢。但如果internalMap
已经是ImmutableMap
,那就完全没问题了。
If you don't restrict your class to returning ImmutableMap
, then you could instead return Collections.unmodifiableMap
like so:
如果你不限制你的班级返回ImmutableMap
,那么你可以Collections.unmodifiableMap
像这样返回:
public Map<String, Integer> foo() {
return Collections.unmodifiableMap(internalMap);
}
Note that this is an immutable viewinto the map. If internalMap
changes, so will a cached copy of Collections.unmodifiableMap(internalMap)
. I still prefer it for getters, however.
请注意,这是地图中的不可变视图。如果internalMap
更改,缓存的Collections.unmodifiableMap(internalMap)
. 然而,我仍然更喜欢它的吸气剂。
回答by Solubris
This is not answering the exact question, but it is still worth considering whether a map should be returned at all. If the map is immutable, then the primary method to be provided is based on the get(key):
这并没有回答确切的问题,但仍然值得考虑是否应该返回地图。如果映射是不可变的,则提供的主要方法是基于 get(key):
public Integer fooOf(String key) {
return map.get(key);
}
This makes the API much tighter. If a map is actually required, this could be left up to the client of the API by providing a stream of entries:
这使得 API 更加紧密。如果确实需要地图,则可以通过提供条目流将其留给 API 的客户端:
public Stream<Map.Entry<String, Integer>> foos() {
map.entrySet().stream()
}
Then the client can make its own immutable or mutable map as it needs, or add the entries to its own map. If the client needs to know if the value exists, optional can be returned instead:
然后客户端可以根据需要制作自己的不可变或可变映射,或者将条目添加到自己的映射中。如果客户端需要知道该值是否存在,则可以返回 optional 代替:
public Optional<Integer> fooOf(String key) {
return Optional.ofNullable(map.get(key));
}
回答by toro
Immutable Map is a type of Map. So leaving the return type of Map is okay.
不可变 Map 是 Map 的一种。所以保留 Map 的返回类型是可以的。
To ensure that the users do not modify the returned object, the documentation of the method can describe the characteristics of the returned object.
为了保证用户不会修改返回的对象,方法的文档可以描述返回对象的特性。
回答by WSimpson
This is arguably a matter of opinion, but the better idea here is to use an interface for the map class. This interface doesn't need to explicitly say that it is immutable, but the message will be the same if you don't expose any of the setter methods of the parent class in the interface.
这可以说是见仁见智,但这里更好的主意是为地图类使用接口。这个接口不需要显式声明它是不可变的,但是如果你没有在接口中暴露父类的任何 setter 方法,消息将是相同的。
Take a look at the following article:
看看下面的文章: