我如何告诉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"} 

没有办法告诉编译器。 变量的类型在声明时确定。 在这种情况下,变量被声明为可以包含空值的数组。

fill()方法不声明新的变量,它只修改现有的变量的内容,所以它不能导致变量类型改变。

为什么填充阵列工作正常

被填充的数组在第二个参数的lambda调用期间推断数组的类型:

 val strings = Array(10000, {"string"}) 

产生Array<String>

 val strings = Array(10000, { it -> if (it % 2 == 0) "string" else null }) 

产生Array<String?>

因此,将该声明更改为与lambda不匹配的左侧不会有任何帮助。 如果发生冲突,则会出现错误。

如何使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> } 

然后使用这个函数新的函数来做转换(元素检查为非空投):

 val nullableStrings = arrayOfNulls<String>(10000).apply { fill("hello") } val strings = nullableStrings.asNotNull() // magic! val upperStrings = strings.map { it.toUpperCase() } // no error 

但是,即使谈到最后的​​选择,我也觉得很肮脏。