Kotlin中first方法返回非空?IntelliJ为何判定userLastName永不为空
我编写了如下代码,用于从列表中选取第一个lastName字段值不为UNKNOWN或MISSING的项,并获取其lastName:
val userLastName = someList .first { it.lastName != "UNKNOWN" && it.lastName != "MISSING" } .lastName
但IntelliJ提示userLastName永远不可能为空,这是为什么?如果列表中所有对象的lastName字段值都是UNKNOWN或MISSING,那么userLastName应该会为空才对?
我尝试改为使用空安全运算符:
val userLastName = someList .first { it.lastName != "UNKNOWN" && it.lastName != "MISSING" } ?.lastName
却收到警告:
对非空接收者使用安全调用,在未来版本中会得到可空类型
问题解析
1. 为何userLastName被判定为永远非空?
Kotlin标准库的List.first(predicate: (T) -> Boolean)方法不会返回null:当列表中没有符合条件的元素时,它会直接抛出NoSuchElementException,而非返回null。只要代码正常执行(未抛出异常),first()必然返回一个非空元素,因此调用其lastName得到的结果也必然非空(前提是lastName本身为非空类型)。你假设的"所有元素不符合条件时返回null"的情况不会发生,此时程序会直接崩溃。
2. 为何空安全运算符触发警告?
因为first()的返回值是非空类型(T而非T?),对非空值使用?.安全调用属于冗余写法——编译器明确知道该接收者不可能为null,因此会提示该写法无意义,且未来版本会将其处理为可空类型,可能引发类型不匹配问题。
正确解决方案
若想避免异常,同时在无符合条件元素时得到null,应使用firstOrNull(predicate: (T) -> Boolean)方法:
val userLastName = someList .firstOrNull { it.lastName != "UNKNOWN" && it.lastName != "MISSING" } ?.lastName
firstOrNull()在找不到符合条件的元素时返回null,此时使用?.安全调用获取lastName完全合理,编译器也不会再给出警告。
内容的提问来源于stack exchange,提问作者firstpostcommenter

