Scala 异常处理
Scala 异常处理
Section titled “Scala 异常处理”与 Java 和其他语言类似,Scala 使用异常(exceptions)来处理中断程序正常流程的错误。方法可以通过抛出异常(throwing an exception)而不是返回值来表示错误。
Scala 没有受检异常(checked exceptions)。这意味着编译器不会强制你声明或捕获特定的异常,这提供了更大的灵活性,但也需要仔细考虑潜在的运行时错误。
抛出异常与 Java 完全相同:创建异常对象并使用 throw 关键字。
def checkAge(age: Int): Unit = { if (age < 0) { throw new IllegalArgumentException(s"Age cannot be negative: $age") } println(s"Age $age is valid.")}
// 示例用法:// checkAge(25) // 输出 "Age 25 is valid."// checkAge(-5) // 抛出 IllegalArgumentException捕获异常 (try-catch):
Section titled “捕获异常 (try-catch):”Scala 使用 try/catch 块来处理异常。catch 块利用模式匹配来处理不同类型的异常。
import java.io.{FileNotFoundException, FileReader, IOException}import scala.util.Using // 推荐用于资源管理
@main def CatchExceptionDemo(): Unit = { try { // 使用 'Using' 确保 FileReader 自动关闭 Using(new FileReader("input.txt")) { reader => // 处理文件内容...(此处未展示) println("File opened successfully.") } } catch { case ex: FileNotFoundException => println(s"Error: File not found - ${ex.getMessage}") case ex: IOException => println(s"Error: I/O problem - ${ex.getMessage}") case ex: Exception => // 最后捕获更广泛的异常 println(s"An unexpected error occurred: ${ex.getMessage}") }}运行此代码(假设 input.txt 不存在):
$ scala CatchExceptionDemoError: File not found - input.txt (The system cannot find the file specified)catch 块会按顺序将抛出的异常与 case 模式进行匹配。FileNotFoundException 会首先被捕获。如果发生了并非 FileNotFoundException 的 IOException,则第二个 case 会匹配。最后一个 case ex: Exception 作为其他潜在异常的“全捕获”(catch-all)。
资源管理:Using 和 finally
Section titled “资源管理:Using 和 finally”释放文件或网络连接等资源至关重要,即使发生异常。
-
scala.util.Using(推荐): 在 Scala 2.13 中引入,Using提供了一种函数式且安全的方式来管理具有close()方法的资源。它确保无论操作成功与否或是否抛出异常,都会调用close()方法。(参见上面的示例)。 -
finally子句: 你可以使用传统的finally块,它始终在try块(以及任何运行过的catch块)之后执行,无论是否抛出了异常。它适用于清理代码。
import java.io.{FileNotFoundException, FileReader, IOException}
@main def FinallyDemo(): Unit = { var reader: Option[FileReader] = None // 使用 Option 处理潜在的 null 值 try { reader = Some(new FileReader("input.txt")) println("File opened (using finally example).") // 处理文件... } catch { case ex: FileNotFoundException => println("File not found (finally example).") case ex: IOException => println("IO Exception (finally example).") } finally { println("Entering finally block...") reader.foreach { r => try { r.close() } catch { case _: IOException => /* 处理关闭错误?*/ } println("FileReader closed.") } }}输出(如果文件未找到):
$ scala FinallyDemoFile not found (finally example).Entering finally block...尽管 finally 有效,但通常更推荐使用 Using,因为它更简洁,并且与函数式编程风格更好地集成。
函数式错误处理(Try,Either):
Section titled “函数式错误处理(Try,Either):”典型的 Scala 风格(Idiomatic Scala)通常更倾向于使用函数式方法处理可恢复错误,而非异常。考虑使用 scala.util.Try、scala.util.Either 或 Option 来表示可能失败的计算:
Option[T]: 表示一个可能存在(Some[T])或不存在(None)的值。适用于可选结果。Try[T]: 表示一个计算结果,它要么是包含一个值的成功(Success[T]),要么是包含一个异常的失败(Failure[Throwable])。对于包装可能抛出异常的代码很有用。Either[A, B]: 表示一个可以是两种类型之一的值,习惯上用Left[A]表示错误,用Right[B]表示成功。比Try更通用,因为错误类型不固定为Throwable。
import scala.util.{Try, Success, Failure}import scala.io.Source
def readFileContent(path: String): Try[String] = Try { Using.resource(Source.fromFile(path))(_.mkString)}
@main def TryDemo(): Unit = { readFileContent("input.txt") match { case Success(content) => println(s"File content:\n$content") case Failure(ex) => println(s"Failed to read file: ${ex.getMessage}") }
readFileContent("existing_file.txt") match { // 假设此文件存在 case Success(content) => println(s"Successfully read: ${content.take(20)}...") case Failure(_) => println("This shouldn't happen if file exists") }}使用 Try、Either 或 Option 允许你作为正常程序流程的一部分显式地处理潜在的失败,这通常能写出更健壮、更易于组合的代码。