Map 中 List 类型的 Kotlin 可空接收器?

Aar*_*ron 1 kotlin

在 Kotlin 中,从映射访问值会在不同的访问方式上提供不同的接收器类型

Map的类型是LinkedHashMap类型

如果我不检查这一行为空

println("Morning: ${daypartEvents[Daypart.MORNING].size}")

编译器给出错误

List 类型的可空接收器上只允许安全 (?.) 或非空断言 (!!.) 调用?

但是,如果尝试使用 forEach 函数迭代映射来获得相同的结果,如下所示

daypartEvents.forEach { (daypart, events) -> println("$daypart: ${events.size}") }

它不希望我对它进行空检查。

即使我做空检查它说

对 List 类型的非空接收器进行不必要的安全调用

我知道这个问题的框架很糟糕,所以请耐心等待

我在这里想知道的是为什么 List 类型的映射的值首先可以为空,然后不能为空。

为了清晰起见,完整代码

enum class Daypart {
    MORNING, AFTERNOON, EVENING
}

data class Event (
    val title: String,
    val description: String? = null,
    val daypart: Daypart,
    val duration: Int
)

fun main() {
    
    val event1 = Event(title = "Wake up", description = "Time to get up", daypart = Daypart.MORNING, duration = 0)
    val event2 = Event(title = "Eat breakfast", daypart = Daypart.MORNING, duration = 15)
    val event3 = Event(title = "Learn about Kotlin", daypart = Daypart.AFTERNOON, duration = 30)
    val event4 = Event(title = "Practice Compose", daypart = Daypart.AFTERNOON, duration = 60)
    val event5 = Event(title = "Watch latest DevBytes video", daypart = Daypart.AFTERNOON, duration = 10)
    val event6 = Event(title = "Check out latest Android Jetpack library", daypart = Daypart.EVENING, duration = 45)
    
    val events = mutableListOf<Event>(event1, event2, event3, event4, event5, event6)
    
    
    val daypartEvents = events.groupBy {
        it.daypart
    }
    
    print(daypartEvents::class.simpleName)
    
    
    println("Morning: ${daypartEvents[Daypart.MORNING]?.size}")
    
    daypartEvents.forEach { (daypart, events) ->
        println("$daypart: ${events.size}")
    }
    
}
Run Code Online (Sandbox Code Playgroud)

我对 Kotlin 还很陌生,这是developer.android.com 上的 Android Jetpack Compose 初学者课程的练习之一

Jof*_*rey 5

一般来说,地图可能有也可能没有特定的键。因此,即使值的类型不可为 null,如果键不在映射中,通过键访问值的表达式也可能返回 null。编译器对代码的分析不够深入,无法理解在您的特定情况下,密钥Daypart.MORNING始终存在。

这就是为什么在你的第一种情况下daypartEvents[Daypart.MORNING]可以为空。

第二种情况有所不同。您并不是试图访问地图中的特定键。您对当前地图中实际存在的所有映射(条目)进行迭代。对于这些,我们知道它们的值不能为 null,因此不需要进行 null 检查。

回到第一种情况,如果密钥不存在,您还可以选择获取异常(失败)而不是 null。正如 @JSmonk 所指出的,这是通过使用daypartEvents.getValue(Daypart.MORNING)而不是括号语法来完成的。错误消息将说明哪个键不在地图中。当您确定它们的密钥始终位于该点的地图中时,或者只是如果您无论如何都打算失败并且此错误适合您时,这很有用。