【问题标题】:Why filtering out null map keys doesn't change type to not nullable in kotlin?为什么过滤掉空映射键不会在 kotlin 中将类型更改为不可空?
【发布时间】:2020-03-27 23:55:18
【问题描述】:

我有一个对象列表,其中一个可选的 id 为 String,我想用它制作一张地图。 我想让我的地图的键不可为空:所以像这样:

data class Foo(
  val id: String? = null
  val someStuff: String? = null,
)

val foo = listOf(Foo("id1"), Foo())
val bar = foo.filterNot { it.id == null }.associateBy { it.id }

这里bar 类型是Map<String?, Foo> 但不是Map<String, Foo>

我的解决方法是添加一个非空断言调用:!!,但它看起来并不干净。

有没有简单安全的方法来做到这一点?

【问题讨论】:

    标签: kotlin


    【解决方案1】:

    这看起来像是contracts 可以帮助解决的问题,但目前contract 表达式无法访问正在使用的类的属性。

    作为一种解决方法,您可以定义一个具有非空id 的第二类,就像这样

    data class Foo(
        val id: String? = null,
        val someStuff: String? = null
    )
    
    data class Foo2(
        val id: String,
        val someStuff: String? = null
    )
    
    val foo = listOf(Foo("id1"), Foo())
    val bar = foo
        .mapNotNull { if (it.id != null) Foo2(it.id, it.someStuff) else null }
        .associateBy { it.id }
    

    【讨论】:

    • 我的 Foo 类有很多字段,复制它不是一个非常方便的解决方法,但我明白你的意思是 mapNotNull 可以解决问题,谢谢!
    【解决方案2】:

    six-year-old open feature request 对应 Map.filterNotNullKeys()four-year old open feature request 对应 Map.associateByNotNull()

    在我看来,associateBy { it.id!! } 对于可读性来说是最干净的。但你可以这样做:

    val bar = foo.mapNotNull { it.id?.run { it.id to it } }.toMap()
    

    至于您的实际问题,该逻辑对于编译器而言要推断的步骤太多。您对associateBy 的最后一个函数调用看到一个可为空的,因此它推断为一个可为空的。为了让编译器弄清楚这一点,它必须退后一步,看到您调用 associateBy 的 List 恰好以某种方式过滤掉了某些对象,以确保某个可为空的属性不会为空在此特定列表中,它与您关联的属性相同。现在想象一下,每次调用任何通用函数时都必须这样做,并且所涉及的各种 lambdas 可能有多行代码。编译时间会飙升。

    【讨论】:

      猜你喜欢
      • 2022-01-21
      • 1970-01-01
      • 2022-09-27
      • 2022-07-13
      • 2021-05-09
      • 1970-01-01
      • 1970-01-01
      • 2023-03-11
      • 1970-01-01
      相关资源
      最近更新 更多