在下面的示例中,函数应返回非空数据。 由于数据可以在流程中更改,因此需要为var,并且只能以可以为空为止。
我无法使用lateinit
,因为if (d == null)
的第一次调用将会抛出。
在该过程之后,它将被分配一个非空数据,但返回必须使用!!
(双重或非空断言运算符)。
避免!!
的最佳方法是什么?
fun testGetLowest (dataArray: List<Data>) : Data {
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return d!!
}
答案 0 :(得分:2)
如果您不喜欢!!
,请为其提供默认值。您将意识到,如果列表不为空,您只能提供默认值,但正如您所说,已知列表非空。这个故事的好处是类型系统没有跟踪列表大小,所以当你说dataArray[0]
时,它会接受你的话。
fun testGetLowest(dataArray: List<Data>) : Data {
var d: Data = dataArray[0]
for (i in 1 until dataArray.size) {
if (d.level < dataArray[i].level) {
d = dataArray[i]
}
}
return d
}
答案 1 :(得分:1)
即使你要将它转换为Option
,当dataArray
为空时你仍然需要处理这种情况,因此返回的值是未定义的。
如果你想让它成为一个完整的函数而不是抛出异常,你可以返回Option<Data>
而不是Data
,这样空dataArray
的情况就会返回一个None
并将其留给来电者来处理如何处理悲伤的道路。
答案 2 :(得分:1)
通常,您可以而且应该依靠编译器来推断可空性。这并不总是可行的,并且在设计的示例中,如果内循环运行但是d
非空。如果dataArray
至少有一个成员,则可以保证这一点。
使用这些知识,您可以使用require稍微重构代码来检查参数(对于至少一个数组成员)和checkNotNull来声明dataArray
的状态为一个后置条件。
fun testGetLowest (dataArray: List<Data>) : Data {
require(dataArray.size > 0, { "Expected dataArray to have size of at least 1: $dataArray")
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return checkNotNull(d, { "Expected d to be non-null through dataArray having at least one element and d being assigned in first iteration of loop" })
}
请记住,您可以返回checkNotNull
(及类似运算符)的结果:
val checkedD = checkNotNull(d)
请参阅Google Guava的Preconditions,了解相似内容。
答案 3 :(得分:0)
fun testGetLowest(dataArray: List<Data>)
= dataArray.minBy { it.level } ?: throw AssertionError("List was empty")
这使用?:
运算符来获取最小值,或者如果最小值为null(列表为空)则抛出错误。
答案 4 :(得分:0)
接受的答案完全没问题,只是提到了另一种通过更改代码中的一行来解决问题的方法:return d ?: dataArray[0]