Kotlin问题:复制Map时withDefault默认值未被保留
Kotlin中Map.withDefault默认值复制后丢失的原因解析
为什么默认值包装在复制时不被保留?
withDefault方法不会修改原Map对象,而是返回一个名为MapWithDefault的包装类实例。这个包装类仅负责拦截getValue调用,当键不存在时触发默认值逻辑,但它本身不存储任何键值对,所有数据都指向底层的原Map。- 调用
toMap()时,方法会遍历包装类持有的底层Map的所有键值对,创建一个新的标准Map实现(比如HashMap)。而MapWithDefault的默认值逻辑属于包装类自身,不会被带入新创建的标准Map——因为标准Map并没有内置存储默认值的机制。
这是预期行为吗?
是的,这完全符合Kotlin的设计预期。withDefault的定位是为单次或局部的getValue调用提供临时默认逻辑,而非让Map本身永久绑定默认值。官方设计逻辑中,默认值仅在通过该包装类调用getValue时生效,所有涉及Map数据复制、遍历的操作,都只会作用于底层的原始Map数据。
容易忽略的细节
MapWithDefault是装饰器模式的实现,它实现了Map接口,但所有不涉及getValue的操作(比如containsKey、entries遍历)都会直接转发给底层Map。- 如果需要在复制后保留默认值逻辑,有两种方案:
- 直接传递
MapWithDefault实例,不调用toMap()(如果场景允许直接使用这个包装类) - 在复制后的新Map上重新调用
withDefault:val mapCopy = map.toMap().withDefault { 0 }
- 直接传递
- 另外,
getOrDefault方法和withDefault的getValue逻辑不同:getOrDefault是每次调用时显式传入默认值,不依赖包装类,所以复制后的Map也可以正常使用:mapCopy.getOrDefault(1, 0) // 同样返回0
复现代码
@Test fun `withDefault not preserved when copying a map`() { val map = mapOf<Int, Int>().withDefault { 0 } assertEquals(map.getValue(1), 0) val mapCopy = map.toMap() assertThrows<NoSuchElementException> { mapCopy.getValue(1) } }
内容的提问来源于stack exchange,提问作者Anaís Solís
相关产品推荐
相关产品推荐

