高效的Kotlin——异常与类型安全结果

异常与类型安全结果(Prefer null or Failure result when the lack of result is possible)

你可能觉得,这一条的中英文对不上啊,的确,这个条目以下的内容并非来自于Effective Kotlin这本书,而是来自我看到的一篇文章,我觉得这篇文章与这一条很契合,并且写得更好。所以就引用这篇文章来说明这一点。

这一条的主要含义在于辨析异常与返回错误结果之间的关系。所谓类型安全的结果,会在后文中有解释。

The first and foremost use of exceptions in Kotlin is to handle program logic errors.
异常指向的是程序中的逻辑错误。

异常应该被用来检测程序中的先决条件和约束条件(preconditions and invariants),这些是编译器无法发现的错误。而requirecheck存在的最大意义就在于此。

As a rule of thumb, you should not be catching exceptions in general Kotlin code.
在Kotlin代码中,不应该捕获异常。

一般而言,在Kotlin中捕获异常被认为是一种“脏代码”。异常应该在顶层的架构被处理,主要目的就是提示bug。异常指向的是程序中的逻辑错误,其实就是程序的bug,这是异常在Kotlin中的首要目的。

然而,很多时候区分程序的逻辑错误和程序有必要处理的“边界”条件是困难的。很多时候这都取决于具体情况。
以标准库中的String.toInt()为例,输入非数字格式的字符串是程序的逻辑错误还是边界条件呢?严格来讲,所有非数字格式的字符串作为输入都应该视为逻辑错误并抛出NumberFormatException,如果调用者把这种情况视为一种边界条件,那么他应该捕获NumberFormatException异常并返回自己想要的默认值,这就是典型的Java的做法。如上所述,这非常不Kotlin,地道的Kotlin style应该是使用String.toIntOrNull()

val number = string.toIntOrNull() ?: defaultValue

String.toIntOrNull()使用null作为逻辑异常的结果,然后结合Kotlin null-safety,这就very Kotlin-style。
Kotlin的标准库都遵循这种设计规范,甚至Array和List的get操作符都有相应的getOrNull版本,用null来表示IndexOutOfBoundsException逻辑异常的结果。

Use exceptions for logic errors, type-safe results for everything else. Don't use exceptions as a work-around to sneak a result value out of a function.
用异常来表示逻辑错误,用type-safe(主要就是null和sealed class,见下文)的结果来表示其它。不要使用异常来作为函数除正常返回值之后的的另外一种返回结果(异常不是“逃逸”出函数的方式,它只是用来表示逻辑错误)。

经常的,函数会有成功和失败两种结果,此时我们可以借鉴标准库的做法,一个函数抛出异常来指示逻辑错误,另外一个“配对”函数使用null作为逻辑错误的结果(类似String.toIntOrNull()String.toInt())。如果函数有多种失败的情形(多种逻辑错误),那么可以考虑使用sealed class作为配对函数的返回值。

sealed class ParsedDate {
    data class Success(val date: Date) : ParsedDate()
    data class Failure(val errorOffset: Int) : ParsedDate()
}

fun DateFormat.tryParse(text: String): ParsedDate =
    try {
        ParsedDate.Success(parse(text))
    } catch (e: ParseException) {
        ParsedDate.Failure(e.errorOffset)
    }

结合Kotlin的when表达式,也可以实现类型安全的Kotlin-style的代码。

最后解释一下什么叫类型安全的结果,其实就是返回null或者sealed class,对于null值,Kotlin有空安全作为保障,我们可以继续使用null值或者通过?:操作符使用默认值、return或者throw;而对于sealed class我们也说它是类型安全的,因为通过when表达式,sealed class的每种值都不会被遗漏,所有结果都会得到相应的处理。

Don't use exceptions if you need local handling of certain failure scenarios in your code, don't use exceptions to return a result value, avoid try/catch in general application code, implement centralized exception-handling logic, handle input/output errors uniformly at an appropriate boundary of your code.

这一小节的主要内容来自于Kotlin and Exceptions。作者是目前的Kotlin Leader,写得简明且深刻,对Kotlin中异常的定位做了非常明晰的说明。

你可能感兴趣的:(高效的Kotlin——异常与类型安全结果)