Nothing
类型
Nothing
返回类型表示一个永远不返回的函数
通常这是一个总是抛出异常的函数。
下面是一个产生无限循环(避免使用这种情况)的函数,因为它永远不会返回,其返回类型是 Nothing
:
// NothingType/InfiniteLoop.kt
package nothingtype
fun infinite(): Nothing {
while (true) {}
}
Nothing
是一个内置的 Kotlin 类型,没有实例。
一个实际的例子是内置的 TODO()
,它的返回类型是 Nothing
,并抛出 NotImplementedError
:
// NothingType/Todo.kt
package nothingtype
import atomictest.*
fun later(s: String): String = TODO("later()")
fun later2(s: String): Int = TODO()
fun main() {
capture {
later("Hello")
} eq "NotImplementedError: " +
"An operation is not implemented: later()"
capture {
later2("Hello!")
} eq "NotImplementedError: " +
"An operation is not implemented."
}
虽然 later()
和 later2()
的返回类型都不是 Nothing
,但是 TODO()
返回的是 Nothing
。Nothing
与任何类型都兼容。
later()
和 later2()
可以成功编译。如果调用任何一个,异常会提醒您编写实现。TODO()
是一个有用的工具,用于在填充细节之前“勾勒”代码框架,以验证所有内容是否相互契合。
在下面的代码中,fail()
总是抛出 Exception
,因此它的返回类型是 Nothing
。请注意,调用 fail()
比显式抛出异常更易读和紧凑:
// NothingType/Fail.kt
package nothingtype
import atomictest.*
fun fail(i: Int): Nothing =
throw Exception("fail($i)")
fun main() {
capture {
fail(1)
} eq "Exception: fail(1)"
capture {
fail(2)
} eq "Exception: fail(2)"
}
fail()
允许您轻松更改错误处理策略。例如,您可以更改异常类型或在抛出异常之前记录附加消息。
如果参数不是 String
,则下面的代码会抛出 BadData
异常:
// NothingType/CheckObject.kt
package nothingtype
import atomictest.*
class BadData(m: String) : Exception(m)
fun checkObject(obj: Any?): String =
if (obj is String)
obj
else
throw BadData("Needs String, got $obj")
fun test(checkObj: (obj: Any?) -> String) {
checkObj("abc") eq "abc"
capture {
checkObj(null)
} eq "BadData: Needs String, got null"
capture {
checkObj(123)
} eq "BadData: Needs String, got 123"
}
fun main() {
test(::checkObject)
}
checkObject()
的返回类型是 if
表达式的返回类型。Kotlin 将 throw
视为类型 Nothing
,而 Nothing
可以分配给任何类型。在 checkObject()
中,String
优先于 Nothing
,因此 if
表达式的类型是 String
。
我们可以使用安全转换和 Elvis 运算符重写 checkObject()
。checkObject2()
会将 obj
转换为 String
,如果可以转换,否则会抛出异常:
// NothingType/CheckObject2.kt
package nothingtype
fun failWithBadData(obj: Any?): Nothing =
throw BadData("Needs String, got $obj")
fun checkObject2(obj: Any?): String =
(obj as? String) ?: failWithBadData(obj)
fun main() {
test(::checkObject2)
}
当给出一个没有附加类型信息的纯粹的 null
时,编译器会推断出一个可空的 Nothing
:
// NothingType/ListOfNothing.kt
import atomictest.eq
fun main() {
val none: Nothing? = null
var nullableString: String? = null // [1]
nullableString = "abc"
nullableString = none // [2]
nullableString eq null
val nullableInt: Int? = none // [3]
nullableInt eq null
val listNone: List<Nothing?> = listOf(null)
val ints: List<Int?> = listOf(null) // [4]
ints eq listNone
}
您可以将 null
和 none
都分配给可空类型的 var
或 val
,例如 nullableString
或 nullableInt
。这是允许的,因为 null
和 none
的类型都是 Nothing?
(可空的 Nothing
)。就像 Nothing
类型的表达式(例如 fail()
)可以被解释为“任何类型”一样,Nothing?
类型的表达式,例如 null
,可以被解释为“任何可空类型”。在 [1]、[2] 和 [3] 中显示了不同可空类型的分配。
listNone
初始化为只包含 null
值的 List
。编译器将其推断为 List<Nothing?>
。因此,当您使用只包含 null
的值初始化 List
时,必须显式指定您要存储在 List
中的元素类型([4])。
Exercises and solutions can be found at www.AtomicKotlin.com.