如何告诉Kotlin数组或集合不能包含空值?
如果我创建一个数组,然后填充它,科特林认为,有可能是在阵列中的空值,并迫使我考虑到这一点如何告诉Kotlin数组或集合不能包含空值?
val strings = arrayOfNulls<String>(10000)
strings.fill("hello")
val upper = strings.map { it!!.toUpperCase() } // requires it!!
val lower = upper.map { it.toLowerCase() } // doesn't require !!
创建一个满阵不存在这个问题
val strings = Array(10000, {"string"})
val upper = strings.map { it.toUpperCase() } // doesn't require !!
如何告诉编译器strings.fill("hello")
的结果是一个NonNull数组?
经验法则:如果在疑惑,明确指定类型(有针对特殊重构):
val strings1: Array<String?> = arrayOfNulls<String>(10000)
val strings2: Array<String> = Array(10000, {"string"})
所以你看那个strings1
包含可为空项,而strings2
没有。这一点,只是决定如何使用这些阵列工作:
// You can simply use nullability in you code:
strings2[0] = strings1[0]?.toUpperCase ?: "KOTLIN"
//Or you can ALWAYS cast the type, if you are confident:
val casted = strings1 as Array<String>
//But to be sure I'd transform the items of the array:
val asserted = strings1.map{it!!}
val defaults = strings1.map{it ?: "DEFAULT"}
我以为我试过'作为Array
我遇到的问题是'Array(10000,{“string})'运行代码10000次,而不是将值复制1000次 - 我无法相信它是快速的。”map {it !!} ,虽然在这两种情况下,我认为编译器可能非常聪明 –
当我说'as Array
有没有办法告诉编译器。变量的类型在声明时确定。在这种情况下,该变量被声明为可包含空值的数组。
fill()方法不声明新变量,它只修改现有的变量的内容,所以它不能导致变量类型改变。
因此没有办法将阵列
你总是可以抛出空头,但责任在你身上。 – voddan
为什么填充阵列工作正常
填充阵列从拉姆达作为第二通话过程中推断阵列的类型参数:
val strings = Array(10000, {"string"})
产生Array<String>
val strings = Array(10000, { it -> if (it % 2 == 0) "string" else null })
产生Array<String?>
因此改变声明的=
的不拉姆达没有做任何事情来帮助匹配左边。如果发生冲突,则会出现错误。
如何使arrayOfNulls工作
对于arrayOfNulls
问题,他们键入您指定的号召arrayOfNulls<String>
在函数签名被用作泛型类型T
和功能arrayOfNulls
返回Array<T?>
这意味着空。代码中没有任何内容会更改该类型。 fill
方法仅将值设置到现有数组中。
对此可空元素的数组转换为不可为空的元素的列表,使用方法:
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.filterNotNull()
val upper = strings.map { it.toUpperCase() } // no !! needed
这是很好的,因为你的map
调用转换成一个列表,无论如何,那么为什么不事先转换。现在取决于数组的大小,这可能是高性能的,如果在CPU高速缓存中,副本可能会很快。如果是大的,没有高性能,可以让这个懒:
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.asSequence().filterNotNull()
val upper = strings.map { it.toUpperCase() } // no !! needed
或者,你可以通过做副本留在阵,但实际上这是没有意义的,因为你与map
撤消:
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings: Array<String> = Array(nullableStrings.size, { idx -> nullableStrings[idx]!! })
在Java或Kotlin代码(JetBrains研究了统计数据)中,数组确实不是很常见,除非代码正在进行真正的低级优化。使用列表可能会更好。
鉴于无论如何你可能会以列表结束,也许从那里开始并放弃阵列。
val nullableStrings = listOf("a","b",null,"c",null,"d")
val strings = nullableStrings.filterNotNull()
但是,如果你不能停止追求使用数组,而真的要投一个没有副本...
你总是可以写做两件事情的函数:一是,检查所有值是否为空,如果是,则返回抛出的数组不为null。这有点冒险,但只是因为差异是可以为空才是安全的。
首先,创建于Array<T?>
的扩展功能:
fun <T: Any> Array<T?>.asNotNull(): Array<T> {
if (this.any { it == null }) {
throw IllegalStateException("Cannot cast an array that contains null")
}
@Suppress("CAST_NEVER_SUCCEEDS")
return this as Array<T>
}
然后使用此功能的新功能进行转换(元素选中为NOT NULL施法):
val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") }
val strings = nullableStrings.asNotNull() // magic!
val upperStrings = strings.map { it.toUpperCase() } // no error
但我觉得脏甚至在谈论最后的选择。
我添加了一个更直接解决问题的答案。解释为什么会发生这种情况,以及考虑到上下文(您最终列出了列表,以便为什么不早些拥抱它们)的解决方案,以及如果您无法抗拒的黑客版本来投射阵列。 –