如何避免!!在 returns 不可为 null 的函数中
How to avoid !! in a function which returns a non-nullable
在下面的示例中,函数应该 return 一个非空数据。
由于数据在过程中可能会发生变化,所以需要是var,而且只能是nullable开头。
我不能使用 lateinit
因为 if (d == null)
的第一个调用会抛出。
处理后它会被分配一个非空数据,但是return必须使用!!
(双爆炸或非空断言运算符)。
避免 !!
的最佳方法是什么?
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!!
}
如何进行相同的检查,并覆盖空壳
fun testGetLowest(dataArray: List<Data>)
= dataArray.minBy { it.level } ?: throw AssertionError("List was empty")
这使用 ?:
运算符获取最小值,或者如果最小值为空(列表为空)则抛出错误。
即使将其转换为 Option
,您仍然必须处理 dataArray
为空且值 returned 未定义的情况。
如果你想让它成为一个完整的函数而不是抛出异常,你可以 return 一个 Option<Data>
而不是 Data
这样空的情况下 dataArray
将 return 一个 None
并留给调用者处理如何处理悲伤路径。
如果您不喜欢 !!
,请为其提供默认值。您会意识到,如果列表不为空,您只能提供默认值,但是,正如您所说,已知该列表是非空的。这个故事的好处是类型系统不跟踪列表大小,所以当你说 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
}
通常,您可以而且应该依靠编译器来推断可空性。这并不总是可能的,并且在人为的示例中,如果内部循环运行但一旦 d
为非空。如果 dataArray
至少有一名成员,这肯定会发生。
利用这些知识,您可以使用 require to check the arguments (for at least one member of the array) and checkNotNull 稍微重构代码,将 dataArray
的状态断言为 post 条件。
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" })
}
请记住,您可以 return checkNotNull
(和类似运算符)的结果:
val checkedD = checkNotNull(d)
请参阅 Google Guava 的 Preconditions 了解类似内容。
接受的答案完全没问题,但只是提一下通过更改代码中的一行来解决问题的另一种方法:return d ?: dataArray[0]
在下面的示例中,函数应该 return 一个非空数据。 由于数据在过程中可能会发生变化,所以需要是var,而且只能是nullable开头。
我不能使用 lateinit
因为 if (d == null)
的第一个调用会抛出。
处理后它会被分配一个非空数据,但是return必须使用!!
(双爆炸或非空断言运算符)。
避免 !!
的最佳方法是什么?
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!!
}
如何进行相同的检查,并覆盖空壳
fun testGetLowest(dataArray: List<Data>)
= dataArray.minBy { it.level } ?: throw AssertionError("List was empty")
这使用 ?:
运算符获取最小值,或者如果最小值为空(列表为空)则抛出错误。
即使将其转换为 Option
,您仍然必须处理 dataArray
为空且值 returned 未定义的情况。
如果你想让它成为一个完整的函数而不是抛出异常,你可以 return 一个 Option<Data>
而不是 Data
这样空的情况下 dataArray
将 return 一个 None
并留给调用者处理如何处理悲伤路径。
如果您不喜欢 !!
,请为其提供默认值。您会意识到,如果列表不为空,您只能提供默认值,但是,正如您所说,已知该列表是非空的。这个故事的好处是类型系统不跟踪列表大小,所以当你说 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
}
通常,您可以而且应该依靠编译器来推断可空性。这并不总是可能的,并且在人为的示例中,如果内部循环运行但一旦 d
为非空。如果 dataArray
至少有一名成员,这肯定会发生。
利用这些知识,您可以使用 require to check the arguments (for at least one member of the array) and checkNotNull 稍微重构代码,将 dataArray
的状态断言为 post 条件。
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" })
}
请记住,您可以 return checkNotNull
(和类似运算符)的结果:
val checkedD = checkNotNull(d)
请参阅 Google Guava 的 Preconditions 了解类似内容。
接受的答案完全没问题,但只是提一下通过更改代码中的一行来解决问题的另一种方法:return d ?: dataArray[0]