ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
"异常:try、catch、finally、throw、Nothing" # 异常 [TOC] ## 异常类 Kotlin 中所有异常类都是 `Throwable` 类的子孙类。每个异常都有消息、堆栈回溯信息以及可选的原因。使用 **throw-表达式**来**抛出异常**: ```kotlin fun main() { throw Exception("Hi There!") } ``` 使用 **try-表达式**来**捕获异常**: ```kotlin try { // 一些代码 } catch (e: SomeException) { // 处理程序 } finally { // 可选的 finally 块 } ``` **可以有零到多个 catch 块。finally块可以省略。但是 catch与 finally块至少应该存在一个**。 ### Try 是一个表达式 **与Java不同,try是一个表达式,即它可以有一个返回值**: ```kotlin val a: Int? = try { parseInt(input) } catch (e: NumberFormatException) { null } ``` **try-表达式的返回值是 try块中的最后一个表达式或者是(所有)catch块中的最后一个表达式。finally块中的内容不会影响表达式的结果**。 ``` fun main(args: Array<String>) { val a: Int? = try { 1 //正常运行,返回1 } catch (e: NumberFormatException) { 2 null } finally { 3 //finally块不会影响表达式结果 } val b: Int? = try { 1 throw NumberFormatException() } catch (e: NumberFormatException) { 2 null //捕获异常,返回null } finally { 3 //finally块不会影响表达式结果 } println(a) //输出1 println(b) //输出null } ``` ## 受检的异常 **Kotlin 没有受检的异常**。这其中有很多原因,但我们会提供一个简单的例子。 以下是 JDK 中 `StringBuilder` 类实现的一个示例接口: ``` java Appendable append(CharSequence csq) throws IOException; ``` 这个签名是什么意思? 它是说,每次我追加一个字符串到一些东西(一个 `StringBuilder`、某种日志、一个控制台等)上时我就必须捕获那些 `IOException`。 为什么?因为它可能正在执行 IO 操作(`Writer` 也实现了 `Appendable`)…… 所以它导致这种代码随处可见的出现: ```kotlin try { log.append(message) } catch (IOException e) { // 必须要安全 } ``` 这并不好,参见[《Effective Java》第三版](http://www.oracle.com/technetwork/java/effectivejava-136174.html) 第 77 条:*不要忽略异常*。 Bruce Eckel 在[《Java 是否需要受检的异常?》(Does Java need Checked Exceptions?)](http://www.mindview.net/Etc/Discussions/CheckedExceptions) 中指出: > 通过一些小程序测试得出的结论是异常规范会同时提高开发者的生产力与代码质量,但是大型软件项目的经验表明一个不同的结论——生产力降低、代码质量很少或没有提高。 其他相关引证: * [《Java 的受检异常是一个错误》(Java's checked exceptions were a mistake)](http://radio-weblogs.com/0122027/stories/2003/04/01/JavasCheckedExceptionsWereAMistake.html)(Rod Waldhoff) * [《受检异常的烦恼》(The Trouble with Checked Exceptions)](http://www.artima.com/intv/handcuffs.html)(Anders Hejlsberg) ## Nothing 类型 **在 Kotlin 中 `throw` 是表达式,所以你可以使用它(比如)作为 Elvis 表达式的一部分**: ```kotlin val s = person.name ?: throw IllegalArgumentException("Name required") ``` `throw`表达式的类型是特殊类型 `Nothing`。该类型没有值,而是用于标记永远不能达到的代码位置。在你自己的代码中,你可以使用 `Nothing` 来标记一个永远不会返回的函数: ```kotlin fun fail(message: String): Nothing { throw IllegalArgumentException(message) } ``` 当你调用该函数时,编译器会知道执行不会超出该调用: ```kotlin val s = person.name ?: fail("Name required") println(s) // 在此已知“s”已初始化 ``` 可能会遇到这个类型的另一种情况是类型推断。这个类型的可空变体`Nothing?` 有一个可能的值是 `null`。如果用 `null` 来初始化一个要推断类型的值,而又没有其他信息可用于确定更具体的类型时,编译器会推断出 `Nothing?` 类型: ```kotlin val x = null // “x”具有类型 `Nothing?` val l = listOf(null) // “l”具有类型 `List<Nothing?> ``` 示例: ``` fun main(args: Array<String>) { fun fail(message: String): Nothing { throw IllegalArgumentException(message) } //当调用该函数fail()时,编译器会知道执行不会超出该调用(说白了就是程序不会继续执行) //程序中断,输出 "java.lang.IllegalArgumentException: Name参数错误,不能为null" val name = null val s: String = name ?: fail("Name参数错误,不能为null") println(s) } ``` 运行结果 ``` Exception in thread "main" java.lang.IllegalArgumentException: Name参数错误,不能为null at FileKt$main$1.invoke (File.kt:3) at FileKt.main (File.kt:9) ``` ## Java 互操作性 与 Java 互操作性相关的信息,请参见 [Java 互操作性章节](http://www.kotlincn.net/docs/reference/java-interop.html)中的异常部分。 在Kotlin中,所有异常都是非受检的,意味着编译器不会强迫捕获任何异常(try catch)!因此,在Kotlin中调用一个受检异常的Java方法,不会强迫你去捕获异常: ``` //kotlin代码,调用java方法,append(CharSequence csq) throws IOException; fun render(list: List<*>, to: Appendable) { for (item in list) { //在kotlin中不要求捕获异常,但在Java中会强迫捕获异常IOException to.append(item.toString()) } } ```