【问题标题】:Method call with nullable and not nullable generic array具有可空和不可空泛型数组的方法调用
【发布时间】:2018-11-20 15:00:57
【问题描述】:

我想在 Kotlin 中编写一个函数,该函数接受一个字符串数组并将数组中所有项的长度相加。我想到了这样的事情:

fun sumItems(values: Array<String?>): Int {
    var sum: Int = 0
    values.forEach { if(it != null) sum += it.length }
    return sum
}

这很好用,但不幸的是我不能为Array&lt;String&gt; 调用这个方法,因为我得到一个类型不匹配的错误。我也无法创建函数sumItems(values: Array&lt;String&gt;): Int,因为它具有相同的 JVM 签名。我可以将我的论点转换为Array&lt;String?&gt;,但这是不安全的。

那么在 Kotlin 中有没有更好的方法来做到这一点?

【问题讨论】:

  • 您真的需要该功能吗? fun sumItems(values: Array&lt;String&gt;) : Int 还不够吗?如果没有,那么只需使用Array&lt;out String?&gt; 作为参数类型。 Yoni Gibbs 的回答已经表明了这一点,以及该方法如何更简化......
  • 我会将其声明为sumItems(values: Array&lt;String&gt;),当我需要传递Array&lt;String?&gt; 时,我会使用filterNotNull 对其进行过滤。
  • 我会采取与@m0skit0 相同的路线。这使代码更加清晰和干净;-)
  • 然而 filterNotNull 返回一个列表 :(
  • 调用filterNotNull 的问题在于您正在内存中创建第二个集合。而且您必须执行两次循环。在大多数情况下,这可能可以忽略不计,但如果处理大量数据,这并不是最有效的方法。

标签: generics kotlin


【解决方案1】:

试试这个:

fun sumItems(values: Array<out String?>) = values.sumBy { it?.length ?: 0 }

您可能希望将其改为扩展方法:

fun Array<out String?>.sumItems() = sumBy { it?.length ?: 0 }

这对Array&lt;String&gt;Array&lt;String?&gt; 都有效,因为泛型类型上有out 修饰符。这说明values 参数(或扩展方法的接收者)必须是一个产生可为空字符串的数组。显然Array&lt;String?&gt; 产生可空字符串,因此传入是有效的。但Array&lt;String&gt; 产生可空字符串,因为字符串总是可以转换为可空字符串。这在here有更详细的解释。

【讨论】:

  • 我认为这不是被问到的:“我不能为 Array 调用这个方法,因为我得到一个类型不匹配错误”
  • 您可以为Array&lt;String&gt;Array&lt;String?&gt; 调用它。我会用更多细节更新我的答案..
  • 哦,你是对的,我不太习惯使用 out/in 修饰符,非常好的答案!
  • 虽然是解决问题的正确答案,但我只能建议尽快消除 null 值,以免代码中充满大量的 null 安全调用 ;-)
  • 为什么这对lists 也不起作用?如果我有fun foo(c: MutableCollection&lt;out String&gt;),我可以打电话给foo(mutableListOf&lt;String&gt;()),但不能打电话给foo(mutableListOf&lt;String?&gt;())。还有什么注意事项吗?
【解决方案2】:

即使 Yoni Gibbs 的回答是正确的,我宁愿在这里采取另一条路线,即使用不可为空的类型,例如:

fun sumItems(values: Array<String>) = values.sumBy { it.length }

正如评论中提到的 m0skit0:如果您的列表中确实有 null 值,请在求和之前将它们过滤掉,例如:

val arrayWithNulls = arrayOf("hello", null, "world")
arrayWithNulls.filterNotNull()
              .let(::sumItems)

或者更好:跳过该方法并执行:

arrayWithNulls.filterNotNull()
              .sumBy { it.length } // or .sumBy(String::length)

如果应用现有功能就足够了,为什么还要引入新功能?

尝试尽早从数组中消除空值。否则,您的代码只会变得更加复杂(添加许多 null 安全的东西),这会使代码的可读性降低。这样你也可以跳过filterNotNull

【讨论】:

  • 请注意我对问题本身的评论:调用filterNotNull 会在内存中创建一个新列表。这里的开销很可能可以忽略不计,但如果您正在处理大型数据集,您可能希望避免这种情况。你可以在原始数组上调用asSequence 来解决这个问题。
  • 我认为没有真正的理由将 null 值保留在数组中......性能影响可以忽略不计并且可能完全可以避免 ;-) 即使我们将使用大型数据集(然后我假设甚至没有null 值),我宁愿过滤一次并在其他任何地方使用不可为空的类型......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-20
  • 2017-09-25
  • 1970-01-01
  • 1970-01-01
  • 2019-07-02
相关资源
最近更新 更多