我有下面的 map

Map<Integer, ? extends Collection<Integer>> map

有时有一个列表,有时有一个集合作为值.现在我想获取值,但有一个问题,它无法编译.

Collection<Integer> value = map.getOrDefault(1, Collections.emptyList())

我知道我可以这样做:

Collection<Integer> value = map.get(1);
if (value == null) {
     value = Collections.emptyList();
}

但我有兴趣了解它应该如何与通用getOrDefault方法一起工作

推荐答案

简而言之:您cannot使用getOrDefaultat all,任何"V"被声明为?? extends Anything的 map .克隆JDK源代码或使用自己的take on getOrDefault创建实用方法都无法解决这一问题.

可以想象,getOrDefault实现不会有这个问题.不幸的是,OpenJDK源代码没有正确编写,或者我没有看到一些折衷.不幸的是,"修复它"是向后不兼容的,所以它可能永远不会发生;我们被损坏的getOrDefault卡住了.谢谢你提出这个问题——通过思考,我意识到它坏了.我现在感到很遗憾,在被问及反馈时,我没有对amber dev给出反馈:)

对为什么要这样做的解释有点详细,需要良好的直觉和泛型如何工作的知识.如果你感兴趣,戴上你的思考帽,继续读下go !

那么,为什么?

Map<Integer, ? extends Collection<Integer>> map

好的:这意味着这个map变量可以指向,例如,new HashMap<Integer, List<Integer>>().或者,它可以指向new HashMap<Integer, Set<Integer>>()甚至new HashMap<Integer, Collection<Integer>>().

map.getOrDefault(....)

哦哦.这是不可能的.您需要提供一个默认值,此默认值必须"起作用"regardless of what the map is actually pointing at-它需要与此映射中的值的类型相同.想象一下,你有一个Map<Integer, Set<Integer>>-什么可行?好吧,new HashSet<Integer>()行.但是对于all 3(Set<Integer>List<Integer>Collection<Integer>,当然还有任何其他集合类型,它们的数量是无限的),什么才有效呢?

答案是nothing.

你不能这样做.在这里,你根本不能用getOrDefault.好吧,除非你简单地通过null,字面上来说,这是唯一的"所有类型"的值,但是你应该只写.get(k),而不是.getOrDefault(k, null).

固定版本

这是直接从OpenJDK源代码粘贴的getOrDefault的实现:

  default V getOrDefault(Object key, V defaultValue) {
     V v;
     return (((v = get(key)) != null) || containsKey(key))
       ? v
       : defaultValue;
  }

从根本上说,这永远不会对你有用-如果V是? anything,你就不会有V-那?意思是:我们知道类型是什么,也没有表示它的typevar,所以没有表达式可以匹配.在这里,对getOrDefault的这种理解可以很好地发挥作用:

default <Z super V> getOrDefault(Object key, Z defaultValue) {
  Z z;
  return (((z = get(key) != null) || containsKey(key))
    ? z
    : defaultValue;
}

这里发生的事情是,在这种情况下,虽然映射可能是一个集合或列表等,但您只希望它是Collection<Integer>,实际上您并不"在意",如果默认值不是"V",您就可以了(如果您的映射实际上是Map<Integer, List<Integer>>,您不介意调用getOrDefault是否最终会给您一个不是List<Integer>-Collections.empty()的对象).

因此,我们需要确定存在一些新的类型Z,它是V的超类型(从而保证如果映射中的键is,您会得到一个V,这绝对是一种Z,根据声明,Z是V的超类型),并且有一个默认值,它也绝对是Z类型,从而保证在任何一个"分支"(找到键,但找不到键)中,返回的值至少为Z.

但是,map并不是这样工作的,因此您根本不能使用.getOrDefault.

我不认为此时修改getOrDefault是向后兼容的,所以我认为向openjdk核心团队提交特性请求没有任何意义:他们只会拒绝它,我们坚持使用编写的getOrDefault.但是,如果必须使用静态实用程序方法来执行上述操作,则可以创建静态实用程序方法.请注意,您可能希望以不同的方式编写它—强制转换为原始,在原始模式下执行工作,忽略警告,然后进行清理.理论上,一些Map实现可能会有不同的getOrDefault实现,尽管我很难想象它会是什么样子(比如说,computeIfAbsent绝对有重要的自定义实现,例如在ConcurrentHashMap中).

Java相关问答推荐

我想知道为什么我的控制器给出嵌套响应

无法找到符号错误—Java—封装

Java List with all combinations of 8 booleans

为什么Java的代码工作(if condition内部的实例)

无法在org. openjfx:javafx—fxml:21的下列变体之间进行 Select

如何配置ActiveMQ Artemis以使用AMQP 1.0和其他协议与Java

如何以干净的方式访问深度嵌套的对象S属性?

使用PDFBox从PDF中删除图像

如何在Cosmos DB(Java SDK)中增加默认响应大小

如何在@CsvSource中传递空格作为值

无法将GSON导入到我的JavaFX Maven项目

JavaFX:无论何时显示应用程序,如何更改组件/ node 位置?

循环不起作用只有第一个元素重复

本机方法(JNI)总是编译的吗?

JPA无手术同品种器械可能吗?

在Java中使用StorageReference将数据从Firebase存储添加到数组列表

如何在Spring Security中设置一个任何人都可以打开的主页?

保持标题窗格的箭头可见,即使设置为不可折叠

java.lang.NoSuchMethodError:';org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream$Builder org.apache.poi-poi-ooxml-5.2.4

try 添加;按流派搜索;在Web应用程序上,但没有;I don’我不知道;It’这个代码错了