将 List<T?> 转换或转换为 List<T>?
Cast or convert List<T?> to List<T>?
我正在寻找一种方便的方法来安全地将 List<T?>
转换为 List<T>?
(或者更一般地说,将 Iterable<T?>
转换为 Iterable<T>?
)而不过滤任何元素.
换句话说,如果所有元素都不为空,则取列表。
在 Swift 我可以这样做:
let a: [Int?] = [1, nil, 3]
let b: [Int?] = [1, 2, 3]
let aa = a as? [Int] // nil
let bb = b as? [Int] // [1, 2, 3]
在 Kotlin 中,看起来相似的代码只是进行了未经检查的转换,不会导致 aa
成为 null
:
val a = listOf<Int?>(1, null, 3)
val b = listOf<Int?>(1, 2, 3)
val aa = a as? List<Int> // [1, null, 3]
val bb = b as? List<Int> // [1, 2, 3]
因此,我正在寻找这样的方法:
@Suppress("UNCHECKED_CAST")
fun <T : Any> Iterable<T?>.takeIfAllNotNull(): Iterable<T>? =
takeIf { all { el -> el != null } } as? Iterable<T>
有了它,我可以编写如下代码:
val a = listOf<Int?>(1, null, 3)
val b = listOf<Int?>(1, 2, 3)
val aa = a.takeIfAllNotNull() // null
val bb = b.takeIfAllNotNull() // [1, 2, 3]
我一定是漏掉了什么。标准库没有这样的方法吗?
没有内置函数,但您可以结合使用 filterNotNull
和 takeIf
以获得所需的行为:
val a: List<Int?> = listOf(1, null, 3)
val aa: List<Int>? = a.filterNotNull().takeIf { it.size == a.size }
--
编辑:根据@DrawnRacoon 的建议,仅使用 takeIf:
会更快
val aa: List<Int>? = a.takeIf { null !in a } as List<Int>
这不会创建中间列表,并且会在发现空值时短路。它确实需要不安全的转换来强制编译器更改类型。
在扩展函数中提取它看起来像这样:
@Suppress("UNCHECKED_CAST")
fun <T> Iterable<T?>.takeIfAllNotNull(): Iterable<T>? {
return takeIf { null !in this } as? Iterable<T>
}
看来我们回到了你最初的提议。所以也许答案应该是:
没有,你没有遗漏任何东西。
标准库中没有针对这种确切行为的方法。
触摸问题中发生的事情:
它与仿制药侵蚀有关。在运行时没有泛型,所以转换只是 List
到 List
总是成功,它是 returns 原始对象。
编译器会正确地警告您不安全的转换。忽略此警告可能会导致运行时类型不正确:
val a = listOf(1, null, 3)
val aa = a as? List<Int> // unsafe cast List<Int?> to List<Int>
// aa is now List<Int> but contains a null
您正在将 List<Int?>
转换为 List<Int>
,但运行时无法区分这些类型。
为了安全地转换它,我们实际上必须在某个地方检查列表的内容。最好明确地执行此操作,以便清楚发生了什么,因此在我们的示例中调用了额外的函数。
我正在寻找一种方便的方法来安全地将 List<T?>
转换为 List<T>?
(或者更一般地说,将 Iterable<T?>
转换为 Iterable<T>?
)而不过滤任何元素.
换句话说,如果所有元素都不为空,则取列表。
在 Swift 我可以这样做:
let a: [Int?] = [1, nil, 3]
let b: [Int?] = [1, 2, 3]
let aa = a as? [Int] // nil
let bb = b as? [Int] // [1, 2, 3]
在 Kotlin 中,看起来相似的代码只是进行了未经检查的转换,不会导致 aa
成为 null
:
val a = listOf<Int?>(1, null, 3)
val b = listOf<Int?>(1, 2, 3)
val aa = a as? List<Int> // [1, null, 3]
val bb = b as? List<Int> // [1, 2, 3]
因此,我正在寻找这样的方法:
@Suppress("UNCHECKED_CAST")
fun <T : Any> Iterable<T?>.takeIfAllNotNull(): Iterable<T>? =
takeIf { all { el -> el != null } } as? Iterable<T>
有了它,我可以编写如下代码:
val a = listOf<Int?>(1, null, 3)
val b = listOf<Int?>(1, 2, 3)
val aa = a.takeIfAllNotNull() // null
val bb = b.takeIfAllNotNull() // [1, 2, 3]
我一定是漏掉了什么。标准库没有这样的方法吗?
没有内置函数,但您可以结合使用 filterNotNull
和 takeIf
以获得所需的行为:
val a: List<Int?> = listOf(1, null, 3)
val aa: List<Int>? = a.filterNotNull().takeIf { it.size == a.size }
--
编辑:根据@DrawnRacoon 的建议,仅使用 takeIf:
会更快val aa: List<Int>? = a.takeIf { null !in a } as List<Int>
这不会创建中间列表,并且会在发现空值时短路。它确实需要不安全的转换来强制编译器更改类型。
在扩展函数中提取它看起来像这样:
@Suppress("UNCHECKED_CAST")
fun <T> Iterable<T?>.takeIfAllNotNull(): Iterable<T>? {
return takeIf { null !in this } as? Iterable<T>
}
看来我们回到了你最初的提议。所以也许答案应该是:
没有,你没有遗漏任何东西。
标准库中没有针对这种确切行为的方法。
触摸问题中发生的事情:
它与仿制药侵蚀有关。在运行时没有泛型,所以转换只是 List
到 List
总是成功,它是 returns 原始对象。
编译器会正确地警告您不安全的转换。忽略此警告可能会导致运行时类型不正确:
val a = listOf(1, null, 3)
val aa = a as? List<Int> // unsafe cast List<Int?> to List<Int>
// aa is now List<Int> but contains a null
您正在将 List<Int?>
转换为 List<Int>
,但运行时无法区分这些类型。
为了安全地转换它,我们实际上必须在某个地方检查列表的内容。最好明确地执行此操作,以便清楚发生了什么,因此在我们的示例中调用了额外的函数。