【问题标题】:Cast or convert List<T?> to List<T>?将 List<T?> 转换或转换为 List<T>?
【发布时间】:2020-02-20 16:19:20
【问题描述】:

我正在寻找一种方便的方法来安全地将 List&lt;T?&gt; 转换为 List&lt;T&gt;?(或者,更一般地,将 Iterable&lt;T?&gt; 转换为 Iterable&lt;T&gt;?)而不过滤任何元素。

换句话说,如果所有元素都不为空,则取列表。

在 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]

我一定在这里遗漏了什么。标准库没有这样的方法吗?

【问题讨论】:

  • 所以您要的是 Kotlin 解决方案?在这种情况下,您应该用 [swift] 标记问题——至少我是这样理解 Meta 上的 meta.stackoverflow.com/q/316809/1187415 的。
  • val aa = a as? List&lt;Int&gt; // [1, null, 3] 看起来像一个错误
  • 它看起来像一个错误,但编译器会警告问题中忽略的不安全强制转换。忽略不安全的强制转换可能会导致运行时类型不正确。
  • @MartinR,[swift] 已删除。感谢您指出这一点。

标签: kotlin casting iterable


【解决方案1】:

没有内置函数,但您可以结合 filterNotNulltakeIf 来获得所需的行为:

val a: List<Int?> = listOf(1, null, 3)
val aa: List<Int>? = a.filterNotNull().takeIf { it.size == a.size }

--

编辑:根据@DrawnRacoon 的建议,使用 just 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>
}

看来我们已经回到了您最初的提议。所以也许答案应该是:

,您没有遗漏任何东西。
标准库中没有针对这种确切行为的方法。


触摸问题中发生的事情:
它与泛型侵蚀有关。在运行时没有泛型,所以转换只是 ListList 总是成功的,它返回原始对象。
编译器将正确警告您有关不安全的强制转换。忽略此警告可能会导致运行时类型不正确:

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&lt;Int?&gt; 转换为List&lt;Int&gt;,但运行时无法区分这些类型。
为了安全地转换它,我们实际上必须在某个地方检查列表的内容。最好明确地这样做,以便清楚发生了什么,因此在我们的示例中调用了额外的函数。

【讨论】:

  • 这是一个好方法。也可以做一个扩展功能。 fun &lt;T&gt; List&lt;T?&gt;.takeIfAllNotNull(): List&lt;T&gt;? = this.filterNotNull().takeIf { it.size == this.size }
  • 不用过滤,val aa = a.takeIf { null !in it }
  • @DrawnRaccoon 是的,这实际上会快得多。只有您必须手动将其强制转换为 List,即使它是完全安全的,编译器也会将其视为不安全的强制转换。
  • 哦,是的,智能投射在这里不起作用。此外,问题中也有相同的功能
  • 酷。感谢您的解释。我想出的功能基本上就是解决方案。不知道null !in this,比all { el -&gt; el != null }短很多。
猜你喜欢
  • 1970-01-01
  • 2021-11-26
  • 1970-01-01
  • 2012-02-24
  • 2019-05-20
  • 1970-01-01
  • 2012-02-14
相关资源
最近更新 更多