fph*_*ipe 1 iterable casting kotlin
我正在寻找一种方便的方法来安全地将 a List<T?>to List<T>?(或更一般地说,Iterable<T?>to 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]
Run Code Online (Sandbox Code Playgroud)
在科特林中,同样看代码只是做一个未经检查的演员和工作不会导致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]
Run Code Online (Sandbox Code Playgroud)
因此,我正在寻找这样的方法:
@Suppress("UNCHECKED_CAST")
fun <T : Any> Iterable<T?>.takeIfAllNotNull(): Iterable<T>? =
takeIf { all { el -> el != null } } as? Iterable<T>
Run Code Online (Sandbox Code Playgroud)
有了这个,我可以写我的代码如下:
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]
Run Code Online (Sandbox Code Playgroud)
我一定在这里遗漏了一些东西。标准库没有这样的方法吗?
没有内置功能,但您可以组合filterNotNull并takeIf获得所需的行为:
val a: List<Int?> = listOf(1, null, 3)
val aa: List<Int>? = a.filterNotNull().takeIf { it.size == a.size }
Run Code Online (Sandbox Code Playgroud)
——
编辑:根据@DrawnRacoon 的建议,仅使用 takeIf 会更快:
val aa: List<Int>? = a.takeIf { null !in a } as List<Int>
Run Code Online (Sandbox Code Playgroud)
这不会创建中间列表,并且在发现空值时会短路。它确实需要不安全的强制转换来强制编译器更改类型。
在扩展函数中提取它看起来像这样:
@Suppress("UNCHECKED_CAST")
fun <T> Iterable<T?>.takeIfAllNotNull(): Iterable<T>? {
return takeIf { null !in this } as? Iterable<T>
}
Run Code Online (Sandbox Code Playgroud)
看来我们已经回到了你最初的提议。所以也许答案应该是:
不,你没有遗漏任何东西。
标准库中没有针对这种确切行为的方法。
触及问题中发生的事情:
它与泛型侵蚀有关。在运行时没有泛型所以剧组只是List给List它总是会成功,并返回原来的对象。
编译器会正确警告您有关不安全转换的信息。忽略此警告可能会导致运行时类型不正确:
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
Run Code Online (Sandbox Code Playgroud)
您正在转换List<Int?>,List<Int>但运行时无法区分这些类型。
为了安全地进行转换,我们实际上必须在某个地方检查列表的内容。最好明确地这样做,以便清楚发生了什么,因此在我们的示例中调用了额外的函数。
| 归档时间: |
|
| 查看次数: |
130 次 |
| 最近记录: |