3

我有一个扩展功能,可以过滤掉带有空键或值的条目:

fun <K, V> Map<K?, V?>.filterNotNull(): Map<K, V> = this.mapNotNull { 
   it.key?.let { key -> 
      it.value?.let { value -> 
         key to value 
      }
   }
}.toMap()

这适用于具有可为空键和值的映射:

data class Stuff(val foo: Int)

val nullMap = mapOf<String?, Stuff?>(null to (Stuff(1)), "b" to null, "c" to Stuff(3))
assert(nullMap.filterNotNull().map { it.value.foo } == listOf(3))

但不是在具有不可为空的键或值的情况下:

val nullValues = mapOf<String, Stuff?>("a" to null, "b" to Stuff(3))    
assert(nullValues.filterNotNull().map { it.value.foo } == listOf(3))

Type mismatch: inferred type is Map<String, Stuff?> but Map<String?, Stuff?> was expected
Type inference failed. Please try to specify type arguments explicitly.

有没有办法让我的扩展功能适用于这两种情况,还是我需要提供两个单独的功能?

4

3 回答 3

7

我稍后会弄清楚原因,但添加到地图中是有效的:

fun <K : Any, V : Any> Map<out K?, V?>.filterNotNull(): Map<K, V> = ...
于 2019-11-11T11:05:18.833 回答
2

解决方案

fun <K, V> Map<out K?, V?>.filterNotNull(): Map<K, V> = this.mapNotNull {
    it.key?.let { key ->
        it.value?.let { value ->
            key to value
        }
    }
}.toMap()

对我来说似乎过于复杂。它可以写成

fun <K, V> Map<out K?, V?>.filterNotNull(): Map<K, V> =
    filter { it.key != null && it.value != null } as Map<K, V>

丑陋的演员表是必要的,因为编译器(还)不能推断出键和值都不包含空值。


关于协方差的警告out K?

是的,它提供了使用相同方法的可能性,不仅用于,Map<String?, Stuff?>而且用于Map<String, Stuff?>(键是否可为空)。但这种自由是有代价的。对于已知没有空键的映射,您无需为每个条目支付空比较。

在您的初始解决方案中 - 在 K 上没有协方差 - 编译器可能会阻止您调用该低效方法。那么正确的方法可能是

fun <K, V> Map<K, V?>.filterValuesNotNull() = filterValues { it != null } as Map<K, V>
于 2019-11-14T22:42:41.717 回答
1

使用时可以指定 Map 类型mapOf

assert(mapOf<String?, String?>("x" to null, "a" to "b").filterNotNull() == mapOf("a" to "b"))

编辑

您指定了扩展函数Map<K?, V?>并试图使用它推断Map<String, String>(在您的原始问题中),所以它不会工作,因为Map<String, String>它不是 的子类型Map<K?, V?>,因为地图接口被定义为Map<K, out V>. 它在键参数类型上是不变的,在值参数类型上是协变的。

您可以做的是通过更改Map<K?, V?>为来使扩展函数中的键类型也协变Map<out K?, V?>。现在,Map<String, String>Map<String, String?>将是Map<K? V?>.

您也可以使用 biLet 代替两个嵌套的 let:如何使用 let (或 apply 等) 检查 2 个条件

于 2019-11-11T10:51:19.180 回答