为什么EnumMap<T>.keySet()返回一个Set<T>()而不是一个EnumSet<T>()?

13

我正在写一个有很多枚举的程序,我经常需要返回EnumMap的键集。但是EnumMap.keySet()返回一个Set(),因此为了获得我想要的EnumSet,我不得不使用强制类型转换:

EnumMap<SomeEnum, Object> myMap = getMap();
EnumSet<SomeEnum> myEnum = (EnumSet<SomeEnum>) myMap.keySet();
如果我不进行类型转换,编译器会出现类型不匹配的错误;它无法将Set<SomeEnum>转换为EnumSet<SomeEnum>。似乎没有必要强制转换,因为一个 EnumMap 的键总是枚举类型。有人知道为什么 keySet() 方法是这样构建的吗?我曾经想过这可能与 EnumSet 是抽象类有关,但是肯定 EnumMap 可以返回 EnumSet 工厂方法提供的任何内容。

谢谢大家!

编辑:非常抱歉,上面的代码会引发 CastClassException。您可以通过使用以下代码获取 EnumSet:

EnumSet<SomeEnum> myEnum = EnumSet.copyOf(myMap.keySet());

在发布前我真的应该先检查一下。


5
你获得了一个Set<SomeEnum>。使用EnumSet<SomeEnum>有什么好处? - Guillaume Polet
@PaulBellora 您是正确的,上面的代码是错误的。我会进行更正。应该是 EnumSet<SomeEnum> myEnum = EnumSet.copyOf(myMap.keySet()); - jalopezp
这里还有一个漂亮的源代码视图,带有内联样式的javadoc:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/EnumMap.java#EnumMap.keySet%28%29 - Paul Bellora
4个回答

8

我认为原因是keySet不是EnumSet

之所以不是,是因为keySet只是底层映射的视图。

myMap.keySet().removeAll(keysToRemove); // removes multiple keys.

从概念上讲,keySet() 是一个枚举集合或枚举集。如果 API 的设计不同,我们本可以拥有 EnumSet 接口,但是考虑到这与 Set 相同,实际上没有任何优势。如果 EnumSet 可以在运行时为您提供特定于枚举的方法,例如其元素类型,那么它可能会有所不同。 - Peter Lawrey

3

EnumSet 是一个实现了 Set 接口的类,它使用枚举类型来表示键空间。这个类并没有提供额外的非静态方法,所以没有理由仅仅为了返回一个普通的 Set 而不是 EnumSet


到目前为止,这是最好的答案,而且我认为它很简单。它不涉及实现细节/与API设计无关),也没有错误的假设,即Java没有协变返回类型(子类可以在覆盖方法中返回返回值的子类)。 - Mattias Isegran Bergander
+1 看 API 设计比看底层代码更好。 - Guillaume Polet

2

编辑:我错了,请忽略。

EnumMap扩展自AbstractMap,它声明了keySet。AbstractMap不能声明keySet返回EnumSet,因为这不适用于AbstractMap的其他子类。因此,它的返回类型是Set。


3
不准确,子类可以有更具体的返回值类型。自Java 5以来支持协变返回类型。 - Mattias Isegran Bergander

0

真的很酷,我不知道我可以看到这个的源代码!一直以来我一直在处理API。但是,是的,我看到keySet()方法返回Set keySet,它在超类中定义,所以我别无选择。非常感谢,我会好好享受的! - jalopezp
3
在你给出的链接的800行代码中,我应该在哪里找到答案?请发一段相关部分的摘录。 - Gabe
@Gabe 如果你按照定义的方法正确地进行操作,那么你就能找到解决方案。我无法发布整个代码,否则你将无法理解答案。 - Bhavik Ambani
1
@Gabe 你会注意到从第364行开始定义了Set<K> keySet()方法。超链接指向java.util.Map,从第298行开始。 - jalopezp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接