我想检查
List<Int?>
是否存在空值,如果不存在空值,则允许列表作为 List<Int>
继续,否则使用 emptyList()
这就是我想到的:
var noNullElements: List<Int> = emptyList()
if (listWithPossibleNullElements.all { it != null }) {
noNullElements = listWithPossibleNullElements as List<Int>
}
//do something with noNullElements list
如果这是最好、最惯用的方式,我会感到震惊。
解决方案
下面有很多可以接受的解决方案,但我选择的是解决方案的组合
fun getSafeList(list: List<Int?>): List<Int> =if (null in list) emptyList() else list.requireNoNulls()
这是一个单行扩展功能:
inline fun <reified T : Any> Iterable<T?>.filterNoneNull(): Iterable<T>? =
takeIf { null !in it }?.requireNoNulls()
takeIf {}
- 如果 lambda 计算结果为 false 则返回 null 的作用域函数null !in it
- 使用 in
运算符函数 确定列表是否包含 null
takeIf {}
返回非空值,filterNotNull()
会将元素类型从可空 T?
转换为非空 T
请注意,如果提供的
null
包含 Iterable<T?>
,它将返回 null
,否则返回 List<T>
,以防区分提供的列表为空或包含 null
很重要。可以使用 elvis 运算符提供替代方案。
示例用法 - 在 Kotlin Playground 中运行:
fun main() {
val listA: List<Int?> = listOf(1, 2, 3, null)
// since listA contains 'null', the result is 'null'
println(listA.filterNoneNull())
// prints: null
// provide an alternative, using the elvis operator
println(listA.filterNoneNull() ?: emptyList<Int>())
// prints: []
val listB: List<Int> = listOf(1, 2, 3)
// since listB does not contain null, the same list is returned
println(listB.filterNoneNull())
// prints: [1, 2, 3]
}
inline fun <reified T : Any> Iterable<T?>.filterNoneNull(): Iterable<T>? =
takeIf { null !in it }?.requireNoNulls()
filterNotNull()
替换为 requireNoNulls()
,因为后者不会实例化新列表
检查 contains 是否存在至少一个 null 值:
val noNullElements = if (list.contains(null)) emptyList() else list.map { it!! }
编辑:添加了此扩展功能:
fun List<Int?>.castToIntOrEmpty() =
if (this.contains(null)) emptyList() else this.map { it as Int }
val noNullElements = list.castToIntOrEmpty()
没有 null 元素的示例:
val list: List<Int?> = List(10) { it }
// list is: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
val noNullElements = if (list.contains(null)) emptyList() else list.map { it!! }
println(noNullElements) // Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
一些(或全部)null元素的示例:
val list: List<Int?> = List(10) { if (it % 2 == 0) it else null }
// list is: [0, null, 2, null, 4, null, 6, null, 8, null]
val nullElements = if (list.contains(null)) emptyList() else list.map { it!! }
println(nullElements) // Output: []
我认为你的问题是你想采用通用的
List<Int?>
类型并将其视为 List<Int>
,这意味着对其进行转换。因为 List<Int?>
不是 List<Int>
的子类型(尽管相反是正确的,正如 Joffrey 在评论中指出的那样),在这种情况下你基本上必须执行未经检查的强制转换:
if (list.contains(null)) emptyList else list as List<Int>
编译器不够聪明,无法确定这是运行时的安全转换(并且您必须能够自己做出保证,例如确保列表在检查通过后不会在其他地方被修改以包含 null)所以你会得到一个“uncheckedcast”警告。 有关泛型的文档会进入您的选项,但如果您确实想传入 List<Int?>
并将其视为
List<Int>
,则必须抑制该警告:
可以通过使用 我认为更@Suppress("UNCHECKED_CAST")
注释语句或声明来抑制未经检查的强制转换警告
方法是创建一个新的List<Int>
,而不是尝试将原始列表转换为不同的泛型类型,或者让原始列表提供者传递一个
List<Int>?
(即) null(如果有任何非空元素),这可能只是解决问题 - 但这实际上取决于你在做什么。 您可以轻松创建过滤副本并将其与原始副本进行比较以决定是否使用它,或者编写一个
for
循环构建一个新列表,如果您遇到空值,则该列表会短路,所有这些事情。如果您想先检查并且仅在需要时复制,
lukas.j's 答案有更多示例(包括一些抛出异常的示例,例如
requireNoNulls()
),因此您有很多选择,并且有不同的权衡 -这实际上取决于您正在做什么以及瓶颈在哪里(如果有)。当您使用泛型时,事情可能会变得有点尴尬,因此某些情况最终看起来并不像您希望的那么整洁!